DB-VPS bootstrap

Sets up the dedicated database VPS that hosts PostgreSQL, Redis, MinIO, and Kafka behind the provider’s private network. App-VPS workers and api-server connect via the private IP — public ports are never opened.

When to run

  • First time the DB-VPS is being provisioned.
  • After a host rebuild — bootstrap is idempotent; existing state under /var/lib/tracium-db/ is preserved.

Prerequisites

  • Fresh VPS in the same private-network as the App-VPS, ≥4 GB RAM (8 GB recommended once pgvector and Kafka log dirs grow).
  • Provider’s private IPs known: APP_VPS_PRIVATE_IP, DB_VPS_PRIVATE_IP.
  • Docker engine + docker compose v2 plugin installed.
  • An unprivileged user with the docker group (e.g. belkanov).
  • SSH access on a non-default port (this runbook assumes 222).

Step 1 — copy artefacts to the host

From your workstation, sync the compose file and the bootstrap script to the DB-VPS:

scp -P 222 \
  deploy/docker/docker-compose.prod-db-infra.yml \
  belkanov@<db-vps-host>:/tmp/
 
scp -P 222 \
  deploy/production-runtime/bin/bootstrap-db-vps.sh \
  belkanov@<db-vps-host>:/tmp/

Step 2 — run the bootstrap script

ssh -p 222 belkanov@<db-vps-host>
sudo /tmp/bootstrap-db-vps.sh /home/belkanov

The script:

  1. Creates /var/lib/tracium-db/{postgres,redis,minio,kafka,compose}/.
  2. Pre-sets ownership so first-time container starts don’t trip on bind-mount permissions (PG=UID 999, Kafka/MinIO=UID 1000).
  3. Generates /var/lib/tracium-db/compose/.env with a fresh KAFKA_CLUSTER_ID and leaves placeholders for passwords + private IPs. Skips the write if .env already exists.
  4. Prints the firewall (ufw) rules to copy-paste.

Step 3 — populate .env

Edit /var/lib/tracium-db/compose/.env. Required fields:

POSTGRES_PASSWORD=<openssl rand -base64 32 | tr -d '\n='>
POSTGRES_BIND_ADDR=<DB_VPS_PRIVATE_IP>
REDIS_BIND_ADDR=<DB_VPS_PRIVATE_IP>
MINIO_ROOT_USER=<≥8 chars>
MINIO_ROOT_PASSWORD=<≥32 chars>
MINIO_BIND_ADDR=<DB_VPS_PRIVATE_IP>
KAFKA_ADVERTISED_HOST=<DB_VPS_PRIVATE_IP>
KAFKA_BIND_ADDR=<DB_VPS_PRIVATE_IP>
# KAFKA_CLUSTER_ID was generated by the script — do not change it.

Tune POSTGRES_SHARED_BUFFERS / POSTGRES_EFFECTIVE_CACHE_SIZE per VPS RAM (see deploy/production-runtime/db-vps/.env.example).

Step 4 — bring the stack up

sudo install -m 0644 /tmp/docker-compose.prod-db-infra.yml \
  /var/lib/tracium-db/compose/docker-compose.yml
 
cd /var/lib/tracium-db/compose
docker compose up -d
docker compose ps

All services must report healthy (or started for the *-init jobs).

# verify private-only binding (must be DB_VPS_PRIVATE_IP, never 0.0.0.0)
ss -tlnp | grep -E ':(5432|6379|9000|9092)\b'

Step 5 — open firewall

On the DB-VPS:

APP_VPS_IP=<app-vps-private-ip>
sudo ufw allow from "$APP_VPS_IP" to any port 5432 proto tcp comment 'tracium PG'
sudo ufw allow from "$APP_VPS_IP" to any port 6379 proto tcp comment 'tracium Redis'
sudo ufw allow from "$APP_VPS_IP" to any port 9000 proto tcp comment 'tracium MinIO'
sudo ufw allow from "$APP_VPS_IP" to any port 9092 proto tcp comment 'tracium Kafka'
sudo ufw allow 222/tcp
sudo ufw default deny
sudo ufw enable

Step 6 — verify from App-VPS

From the App-VPS (ssh -p 222 belkanov@tracium.ru):

# Postgres reachable
PGPASSWORD=$POSTGRES_PASSWORD psql -h $DB_VPS_PRIVATE_IP -U tracium -d tracium -c '\dx'
# expect 'vector' extension once migrations have been applied
 
# Redis reachable
redis-cli -h $DB_VPS_PRIVATE_IP ping
 
# MinIO reachable
curl -fsS "http://$DB_VPS_PRIVATE_IP:9000/minio/health/live"
 
# Kafka reachable
docker run --rm apache/kafka:3.9.0 \
  /opt/kafka/bin/kafka-topics.sh \
  --bootstrap-server $DB_VPS_PRIVATE_IP:9092 \
  --list

Step 7 — run migrations

From the App-VPS, point goose at the DB-VPS PG and apply the schema:

GOOSE_DRIVER=postgres \
GOOSE_DBSTRING="host=$DB_VPS_PRIVATE_IP user=tracium password=$POSTGRES_PASSWORD dbname=tracium sslmode=disable" \
goose -dir backend/migrations up

Step 8 — activate App-VPS workers

Edit /home/belkanov/apps/tracium/shared/tracium-deploy.env:

POSTGRES_HOST=<DB_VPS_PRIVATE_IP>
POSTGRES_PORT=5432
REDIS_URL=redis://<DB_VPS_PRIVATE_IP>:6379/0
S3_ENDPOINT=http://<DB_VPS_PRIVATE_IP>:9000
KAFKA_BROKERS=<DB_VPS_PRIVATE_IP>:9092

Then enable the with-db profile on App-VPS:

cd /home/belkanov/apps/tracium/active
COMPOSE_PROFILES=with-db docker compose -f docker-compose.prod-bluegreen.yml up -d

backend-checks and deploy-production should turn green on the next pipeline once the workers are up.

Backups (post-launch, week 1)

WhatWhereHow
PGDB-VPSpg_dump tracium cron daily, retention 14d
MinIODB-VPSrsync /var/lib/tracium-db/minio weekly
KafkaDB-VPSlog dir snapshot weekly (Kafka is event-log, not SOR)
.envDB-VPSmanual copy to encrypted store after each rotation

Rollback

The bootstrap script never deletes data — re-running it is safe. To fully tear down (loses all data):

cd /var/lib/tracium-db/compose
docker compose down
sudo rm -rf /var/lib/tracium-db/

See also

  • deploy/docker/docker-compose.prod-db-infra.yml — the stack itself.
  • deploy/production-runtime/db-vps/.env.example — env template.
  • deploy/production-runtime/bin/bootstrap-db-vps.sh — the script invoked above.
  • Production environment — App-VPS topology this stack pairs with.
  • Live supplier contour — depends on this stack being live before any real-supplier ingest can run.