Skip to content

Latest commit

 

History

History
389 lines (278 loc) · 11.8 KB

deployment.md

File metadata and controls

389 lines (278 loc) · 11.8 KB

UDC-Bot Deployment Guide

Overview

UDC-Bot can be deployed in two ways:

Method Use Case Complexity
Kubernetes (k3s/k8s) Production and dev server Medium-High
Docker Compose Local development or simple single-server deployment Low

Both methods run the same Docker image and use PostgreSQL as the database.

Table of Contents

Prerequisites

Regardless of deployment method, you need:

  • A Discord bot token from the Discord Developer Portal
  • A server or machine with Docker installed
  • The repository cloned locally

Option A: Kubernetes (k3s/k8s)

This is the primary deployment method used for both the production and dev servers.

What You Get

  • Bot container with init-container config rendering
  • PostgreSQL 16 with persistent storage (5 Gi)
  • Adminer with TLS ingress (Traefik + Let's Encrypt)
  • Optionally: automated daily PostgreSQL backups to S3
  • Optionally: secrets managed via External Secrets Operator (1Password)

Step 1: Set Up the Cluster

Install k3s (lightweight Kubernetes) on your server:

curl -sfL https://get.k3s.io | sh -

Verify the cluster is running:

kubectl get nodes

Step 2: Install cert-manager

cert-manager handles TLS certificates for ingress (Adminer):

kubectl apply -f https://github.com/cert-manager/cert-manager/releases/latest/download/cert-manager.yaml

Step 3: Create the Namespace

kubectl apply -f k8s/prod/namespace.yaml

This creates the udc-bot-prod namespace (or udc-bot-dev for the dev environment).

Step 4: Create Secrets

Create Kubernetes secrets for the bot's credentials:

kubectl -n udc-bot-prod create secret generic postgresql-credentials \
  --from-literal=password='YOUR_POSTGRESQL_PASSWORD'

kubectl -n udc-bot-prod create secret generic discord-bot-token \
  --from-literal=identifiant='YOUR_DISCORD_BOT_TOKEN'

kubectl -n udc-bot-prod create secret generic bot-api-keys \
  --from-literal=weather-api-key='YOUR_KEY' \
  --from-literal=ipgeo-api-key='YOUR_KEY' \
  --from-literal=flight-api-key='YOUR_KEY' \
  --from-literal=flight-api-secret='YOUR_SECRET' \
  --from-literal=airlab-api-key='YOUR_KEY'

For automated secret management, see Using 1Password with External Secrets at the end of this section.

Step 5: Deploy ConfigMaps

These contain the bot's configuration templates and settings:

kubectl apply -f k8s/prod/bot-config.yaml
kubectl apply -f k8s/prod/bot-settings-config.yaml

Before applying, edit bot-config.yaml to set your Discord server's channel and role IDs in the Settings.json template. API keys and tokens are injected from secrets automatically via the init container's envsubst.

Step 6: Deploy PostgreSQL

kubectl apply -f k8s/prod/postgresql.yaml

Wait for PostgreSQL to be ready:

kubectl -n udc-bot-prod wait --for=condition=ready pod -l app.kubernetes.io/name=postgresql --timeout=120s

Step 7: Deploy the Bot

kubectl apply -f k8s/prod/bot.yaml

The bot deployment includes:

  • An init container (render-config) that renders Settings.json from the template by substituting environment variables from secrets
  • An init container (wait-for-postgresql) that blocks until PostgreSQL is reachable
  • The main bot container with the rendered config mounted at /app/Settings

Step 8 (Optional): Backups and Adminer

Database backups: You should set up regular PostgreSQL backups using your preferred method. Common options include:

  • pg_dump via a cron job
  • Volume snapshots (if your storage provider supports it)
  • Dedicated backup tools like eeshugerman/postgres-backup-s3
  • Cloud-managed backup (AWS RDS, GCP Cloud SQL, etc.)

The repository includes an S3-based backup manifest — see Using S3 Backups below if you want to use it.

Adminer (database UI):

kubectl apply -f k8s/prod/adminer.yaml

Adminer is exposed via Traefik ingress with TLS at adminer.bot.udc.ovh (prod) or adminer.dev.bot.udc.ovh (dev). Edit the ingress host to match your domain. Access is restricted by IP allowlist.

Verify

kubectl -n udc-bot-prod get pods
kubectl -n udc-bot-prod logs deployment/udc-bot

Updating the Bot

Build and push a new image, then update the image tag in bot.yaml:

docker build -t ghcr.io/unity-developer-community/udc-bot:NEW_TAG .
docker push ghcr.io/unity-developer-community/udc-bot:NEW_TAG

Edit bot.yaml to use the new tag, then re-apply:

kubectl apply -f k8s/prod/bot.yaml

Advanced: Optional Integrations

Using 1Password with External Secrets

The repository includes manifests for managing secrets via External Secrets Operator with 1Password as the backend. This automates secret rotation and avoids storing credentials in manifests.

Step 1: Install the External Secrets Operator

helm repo add external-secrets https://charts.external-secrets.io
helm install external-secrets external-secrets/external-secrets \
  -n external-secrets --create-namespace

Step 2: Configure the 1Password Connect server

Deploy 1Password Connect in your cluster. See the 1Password Connect documentation for setup instructions. You need:

  • A 1Password Connect server running in the cluster
  • A 1password-credentials.json token
  • A ClusterSecretStore resource pointing to your 1Password Connect instance

Example ClusterSecretStore:

apiVersion: external-secrets.io/v1beta1
kind: ClusterSecretStore
metadata:
  name: onepassword
spec:
  provider:
    onepassword:
      connectHost: http://onepassword-connect.onepassword.svc.cluster.local:8080
      vaults:
        my-vault: 1
      auth:
        secretRef:
          connectTokenSecretRef:
            name: onepassword-token
            namespace: onepassword
            key: token

Step 3: Apply the External Secrets manifests

kubectl apply -f k8s/prod/external-secrets.yaml

This creates the following secrets automatically from 1Password:

Secret 1Password Item
postgresql-credentials PostgreSQL password
discord-bot-token Discord bot token
bot-api-keys Weather, IP Geo, Flight, Airlab API keys
postgresql-backup-credentials AWS S3 keys (only needed for S3 backups)

Secrets are refreshed every hour automatically.

Using S3 Backups

The repository includes a backup CronJob that dumps PostgreSQL to an S3 bucket daily. An init container runs pg_dump into a scratch volume and the main container uploads the file with the AWS CLI, so a failure in either step fails the Job.

Prerequisites:

  • An S3 bucket (or S3-compatible storage like MinIO)
  • AWS credentials with write access to the bucket

Step 1: Create the backup credentials secret

kubectl -n udc-bot-prod create secret generic postgresql-backup-credentials \
  --from-literal=AWS_ACCESS_KEY_ID='YOUR_AWS_KEY' \
  --from-literal=AWS_SECRET_ACCESS_KEY='YOUR_AWS_SECRET'

Step 2: Deploy the backup job

Review k8s/prod/postgresql-backup.yaml and update the S3 bucket name and region if needed, then:

kubectl apply -f k8s/prod/postgresql-backup.yaml

The backup runs daily at 00:00 UTC and stores dumps as udcbot_<timestamp>.dump under the environment prefix of the bucket. Retention is not handled by the job: configure an S3 lifecycle rule on the bucket to expire old dumps.

The manifest also contains a PrometheusRule that fires UdcBotBackupFailed (the latest run did not succeed) and UdcBotBackupStale (no success for 36 hours). It requires the Prometheus Operator CRDs and a rule selector matching the release: monitoring label; delete that document from the file if you do not run them.

To test a backup immediately: kubectl -n udc-bot-prod create job --from=cronjob/postgresql-backup backup-test.


Option B: Docker Compose

Simpler setup for local development or single-server deployment.

What You Get

  • Bot container built from source
  • PostgreSQL with persistent volume
  • Adminer (port 8080)

Step 1: Configure Settings

cp DiscordBot/Settings/Settings.example.json DiscordBot/Settings/Settings.json

Edit Settings.json:

  • Set Token to your Discord bot token
  • Set DbConnectionString to Host=db;Port=5432;Database=udcbot;Username=udcbot;Password=123456789
  • Configure channel and role IDs for your Discord server
  • Set API keys for Weather, Flight, etc.

Step 2: Start Everything

docker-compose up --build

Or start only the database (and run the bot from your IDE):

docker-compose up db

Step 3: Verify

  • Bot logs appear in the terminal
  • Adminer is available at http://localhost:8080
  • Database is on localhost:5432

Production Hardening (if using Compose for production)

The default docker-compose.yml uses hardcoded credentials and is meant for local dev. For a production deployment with Docker Compose:

  1. Change all default passwords in the compose file
  2. Use environment variables or a .env file instead of hardcoded values:
environment:
  POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
  1. Restrict Adminer access — either remove it or bind to localhost only:
ports:
  - "127.0.0.1:8080:80"
  1. Set up database backups — use pg_dump via cron, volume snapshots, or a dedicated backup tool. See Using S3 Backups for an example.

  2. Pin image versions — use specific tags instead of latest:

db:
  image: postgres:16
adminer:
  image: adminer:4

Environment Differences

Setting Production (k8s) Dev Server (k8s) Local (Compose)
Namespace udc-bot-prod udc-bot-dev N/A
Bot image Pinned commit SHA latest Built from source
CPU request 100m 50m Unlimited
Memory limit 512Mi 512Mi Unlimited
PostgreSQL storage 5 Gi PVC 5 Gi PVC Docker volume
Backups User's choice User's choice Manual
Secrets Manual or External Secrets Manual or External Secrets Hardcoded / .env file
Adminer TLS ingress + IP allowlist TLS ingress + IP allowlist localhost:8080