Upgrades & Migrations
This guide covers upgrading Boards to newer versions, including running database migrations.
Upgrade Process Overview
Upgrading Boards involves:
- Check release notes for breaking changes
- Backup your database before upgrading
- Run database migrations (if required)
- Update container images for API and worker
- Verify the deployment is healthy
Always run database migrations before deploying new application versions. The new code may depend on schema changes that migrations provide.
Database Migrations
Boards uses Alembic for database migrations. Migrations are included in the backend image and can be run using the boards-migrate CLI.
Migration Commands
# Upgrade to latest version
boards-migrate upgrade head
# Upgrade to a specific revision
boards-migrate upgrade abc123
# Show current database revision
boards-migrate current
# Show migration history
boards-migrate history
# Downgrade one revision (for rollbacks)
boards-migrate downgrade -1
# Downgrade to a specific revision
boards-migrate downgrade abc123
Running Migrations
Docker Compose
Run migrations before updating services:
# Pull new images
docker compose pull
# Run migrations using the new image
docker compose run --rm api boards-migrate upgrade head
# Then update services
docker compose up -d
Or as a one-liner:
docker compose pull && \
docker compose run --rm api boards-migrate upgrade head && \
docker compose up -d
Kubernetes
Option 1: Job (Recommended)
Create a migration job:
# migration-job.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: boards-migrate-v1-2-0
namespace: boards
spec:
ttlSecondsAfterFinished: 3600
template:
spec:
containers:
- name: migrate
image: ghcr.io/weirdfingers/boards-backend:v1.2.0
command: ["boards-migrate", "upgrade", "head"]
env:
- name: BOARDS_DATABASE_URL
valueFrom:
secretKeyRef:
name: boards-secrets
key: database-url
restartPolicy: Never
backoffLimit: 1
Run the migration:
kubectl apply -f migration-job.yaml
kubectl wait --for=condition=complete job/boards-migrate-v1-2-0 -n boards --timeout=300s
kubectl logs job/boards-migrate-v1-2-0 -n boards
Then update deployments:
kubectl set image deployment/boards-api api=ghcr.io/weirdfingers/boards-backend:v1.2.0 -n boards
kubectl set image deployment/boards-worker worker=ghcr.io/weirdfingers/boards-backend:v1.2.0 -n boards
Option 2: Init Container
Add an init container to run migrations on deployment:
spec:
initContainers:
- name: migrate
image: ghcr.io/weirdfingers/boards-backend:v1.2.0
command: ["boards-migrate", "upgrade", "head"]
env:
- name: BOARDS_DATABASE_URL
valueFrom:
secretKeyRef:
name: boards-secrets
key: database-url
Init containers run on every pod start. Alembic handles this gracefully—if migrations are already applied, it's a no-op. However, this adds startup latency.
Cloud Platforms
Cloud Run:
# Run migration as a one-off job
gcloud run jobs create boards-migrate \
--image ghcr.io/weirdfingers/boards-backend:v1.2.0 \
--command "boards-migrate,upgrade,head" \
--set-secrets "BOARDS_DATABASE_URL=boards-database-url:latest" \
--region us-central1
gcloud run jobs execute boards-migrate --region us-central1 --wait
# Then update services
gcloud run services update boards-api --image ghcr.io/weirdfingers/boards-backend:v1.2.0
gcloud run services update boards-worker --image ghcr.io/weirdfingers/boards-backend:v1.2.0
Railway:
# SSH into the API service and run migrations
railway run boards-migrate upgrade head --service boards-api
# Then trigger a redeploy with the new image
Fly.io:
# Run migration via SSH
fly ssh console --app boards-api -C "boards-migrate upgrade head"
# Then deploy new version
fly deploy --app boards-api
fly deploy --app boards-worker
Render:
# Use the shell to run migrations
render shell boards-api
boards-migrate upgrade head
# Then trigger redeploy from dashboard or CLI
Pre-Upgrade Checklist
Before upgrading:
- Read the release notes for the target version
- Check for breaking changes or required configuration updates
- Backup your database
- Test the upgrade in a staging environment
- Plan for rollback if needed
- Schedule maintenance window if required
Backup Database
Docker Compose:
docker compose exec db pg_dump -U boards boards > backup-$(date +%Y%m%d-%H%M%S).sql
Kubernetes:
kubectl exec -n boards postgres-0 -- pg_dump -U boards boards > backup.sql
Managed databases (RDS, Cloud SQL, etc.): Create a snapshot via the provider's console or CLI.
Rollback Procedures
If an upgrade fails:
1. Rollback Application
Docker Compose:
# Edit compose.yaml to use previous version
# Or set BACKEND_VERSION in .env
BACKEND_VERSION=v1.1.0 docker compose up -d
Kubernetes:
kubectl rollout undo deployment/boards-api -n boards
kubectl rollout undo deployment/boards-worker -n boards
2. Rollback Database (If Needed)
Only rollback the database if the migration itself caused issues:
# Downgrade one migration
boards-migrate downgrade -1
# Or restore from backup
psql -U boards boards < backup.sql
Database rollbacks can cause data loss if the migration added columns that now contain data. Always prefer forward-fixes over rollbacks when possible.
Zero-Downtime Upgrades
For zero-downtime upgrades:
1. Ensure Migration Compatibility
Migrations should be backwards compatible:
- Add columns as nullable or with defaults
- Don't remove columns until the next release
- Don't rename columns; add new, migrate data, remove old
2. Rolling Update Strategy
Kubernetes:
spec:
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 0
maxSurge: 1
Docker Compose with multiple replicas:
# Update one at a time
docker compose up -d --no-deps --scale api=2 api
docker compose up -d --no-deps --scale api=1 api
3. Blue-Green Deployment
- Deploy new version alongside old
- Run migrations
- Switch traffic to new version
- Tear down old version
Version Compatibility
Checking Versions
# Check API version
curl https://api.example.com/health
# Returns: {"status": "healthy", "version": "1.2.0"}
# Check database migration version
boards-migrate current
Skipping Versions
You can upgrade across multiple versions—Alembic will run all pending migrations in order:
# From v1.0.0 to v1.3.0
boards-migrate upgrade head
# Runs: v1.0.0 → v1.1.0 → v1.2.0 → v1.3.0 migrations
Troubleshooting
Migration Fails
- Check the error message in logs
- Verify database connectivity
- Check if migration was partially applied:
boards-migrate current - Fix the issue and retry, or rollback
"Revision Not Found"
The migration revision doesn't exist in the new image. Ensure you're using the correct image version.
Database Locked
Another migration or connection is holding a lock:
-- Find blocking queries
SELECT pid, query, state FROM pg_stat_activity WHERE state != 'idle';
-- Terminate if necessary (careful!)
SELECT pg_terminate_backend(pid);
Data Migration Takes Too Long
For large tables:
- Run migrations during low-traffic periods
- Consider breaking into smaller migrations
- Add indexes concurrently:
CREATE INDEX CONCURRENTLY
Automation
CI/CD Pipeline Example
# .github/workflows/deploy.yml
jobs:
deploy:
steps:
- name: Backup database
run: |
# Create backup via your provider's CLI
- name: Run migrations
run: |
docker run --rm \
-e BOARDS_DATABASE_URL=${{ secrets.DATABASE_URL }} \
ghcr.io/weirdfingers/boards-backend:${{ github.ref_name }} \
boards-migrate upgrade head
- name: Deploy
run: |
# Deploy to your platform
Next Steps
- Docker Deployment - Upgrade Docker Compose deployments
- Kubernetes Deployment - Upgrade K8s deployments
- Monitoring - Monitor upgrade health