Vetrix Docs

Upgrades and Migrations

Vetrix uses golang-migrate to manage database schema changes. Migrations run automatically on every server startup before any traffic is accepted.

How migrations work

  1. On startup main.go calls db.Migrate(dsn, migrationsDir).
  2. golang-migrate reads all *.up.sql files from the migrations directory in numerical order.
  3. Only migrations that have not yet been applied are executed.
  4. The schema_migrations table (managed by golang-migrate) tracks applied versions.
  5. If any migration fails, startup aborts — the database is left in its previous consistent state.

Upgrading Vetrix

Docker Compose

# Pull the new image
docker compose pull

# Restart services (migrations run automatically on startup)
docker compose up -d

# Verify readiness
curl http://localhost:3000/api/v1/ready

Kubernetes

# Update image tag in deployment.yaml, then apply
kubectl apply -f deployments/kubernetes/deployment.yaml

# Monitor rollout
kubectl -n vetrix rollout status deployment/vetrix

# Check logs for migration output
kubectl -n vetrix logs -l app.kubernetes.io/name=vetrix --tail=100

Binary (bare-metal)

# Stop the running server
systemctl stop vetrix

# Replace the binary
cp vetrix-new /usr/local/bin/vetrix

# Start (migrations run automatically)
systemctl start vetrix

# Verify
curl http://localhost:3000/api/v1/ready

Rolling back

Vetrix does not apply rollback migrations automatically. To roll back:

  1. Stop the server.
  2. Restore the previous binary.
  3. Manually run the .down.sql migrations you need to undo:
# Using the migrate CLI (https://github.com/golang-migrate/migrate/tree/master/cmd/migrate)
migrate -database "$DATABASE_URL" -path db/migrations down 1
  1. Start the previous binary.

Warning: Not all migrations are reversible. Always test rollbacks in a staging environment first. Take a database backup before any upgrade to production.

Backup before upgrading

PostgreSQL (pg_dump)

# Dump to compressed file
pg_dump "$DATABASE_URL" -Fc -f vetrix_backup_$(date +%Y%m%d_%H%M%S).dump

# Restore
pg_restore -d "$DATABASE_URL" --clean vetrix_backup_YYYYMMDD_HHMMSS.dump

Repository data

# Tar the repositories directory
tar -czf repos_backup_$(date +%Y%m%d).tar.gz /data/repositories

# Or rsync to a remote host
rsync -az /data/repositories/ backup-host:/backups/vetrix/repositories/

Docker volume backup

# Backup a Docker volume to a local tar archive
docker run --rm \
  -v vetrix_vetrix_data:/data:ro \
  -v $(pwd):/backup \
  alpine tar -czf /backup/vetrix_data_$(date +%Y%m%d).tar.gz /data

Migration directory

Migrations live in db/migrations/ as numbered pairs:

000001_create_users.up.sql
000001_create_users.down.sql
000002_create_ssh_keys.up.sql
...

The VETRIX_MIGRATIONS_DIR environment variable controls where the server looks for them. In Docker, this is /app/db/migrations (embedded in the image at build time).

Checking migration status

migrate -database "$DATABASE_URL" -path db/migrations version
# Prints the currently applied migration version number.

migrate -database "$DATABASE_URL" -path db/migrations status
# Prints each migration and whether it has been applied.

Zero-downtime upgrades

For zero-downtime upgrades with Kubernetes:

  1. New migrations must be backwards-compatible with the previous binary (additive only — no destructive column removals until the old version is fully retired).
  2. Deploy the new version — Kubernetes waits for the readiness probe before shifting traffic.
  3. Once the new deployment is healthy, the old pod is terminated.

This pattern ("expand/contract") is required for multi-replica setups where both old and new pods may run simultaneously during a rolling update.