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
- On startup
main.gocallsdb.Migrate(dsn, migrationsDir). golang-migratereads all*.up.sqlfiles from the migrations directory in numerical order.- Only migrations that have not yet been applied are executed.
- The
schema_migrationstable (managed by golang-migrate) tracks applied versions. - 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:
- Stop the server.
- Restore the previous binary.
- Manually run the
.down.sqlmigrations 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
- 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:
- New migrations must be backwards-compatible with the previous binary (additive only — no destructive column removals until the old version is fully retired).
- Deploy the new version — Kubernetes waits for the readiness probe before shifting traffic.
- 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.