Skip to main content

Backup & Restore

Cronicorn stores all state in PostgreSQL. Backups are your responsibility as the operator — Cronicorn does not include built-in backup tooling, just like it doesn't include a built-in reverse proxy.

What to Back Up​

ItemLocationWhy
DatabasePostgreSQL containerAll jobs, endpoints, runs, users, settings
.envAlongside docker-compose.ymlAuth secrets, API keys, configuration
docker-compose.override.ymlAlongside docker-compose.ymlCustom ports, resources, volumes

Database Backup​

Use pg_dump to create a compressed database dump:

docker exec cronicorn-db \
pg_dump -U user -d db --no-owner --no-acl \
| gzip > cronicorn-backup-$(date +%Y%m%d).sql.gz

Adjust -U user and -d db if you've customized POSTGRES_USER or POSTGRES_DB.

Automating with Cron​

# Edit crontab
crontab -e

# Add daily backup at 2am
0 2 * * * docker exec cronicorn-db pg_dump -U user -d db --no-owner --no-acl | gzip > /path/to/backups/cronicorn-$(date +\%Y\%m\%d).sql.gz 2>&1

Retention​

Pair with a cleanup rule to avoid filling disk:

# Delete backups older than 30 days
find /path/to/backups -name "cronicorn-*.sql.gz" -mtime +30 -delete

Restore Procedure​

1. Stop application services​

Stop everything except the database:

docker compose stop api scheduler ai-planner web docs

2. Restore the database​

# Drop and recreate the database
docker exec cronicorn-db psql -U user -c "DROP DATABASE IF EXISTS db;"
docker exec cronicorn-db psql -U user -c "CREATE DATABASE db;"

# Restore from backup
gunzip -c /path/to/backups/cronicorn-20250115.sql.gz | \
docker exec -i cronicorn-db psql -U user -d db

3. Restart services​

docker compose up -d

The migrator will run but should find no pending migrations (since the backup already contains the schema).

4. Verify​

# Check health
curl http://localhost:3333/api/health

# Log in and verify jobs, endpoints, and recent runs are present

Backup Verification​

Periodically test that your backups are restorable:

# Start a temporary database
docker run --name pg-test -e POSTGRES_USER=user -e POSTGRES_PASSWORD=password -e POSTGRES_DB=db -d postgres:17

# Restore backup
gunzip -c /path/to/backups/cronicorn-20250115.sql.gz | \
docker exec -i pg-test psql -U user -d db

# Check tables exist and have data
docker exec pg-test psql -U user -d db -c "\dt"
docker exec pg-test psql -U user -d db -c "SELECT count(*) FROM jobs;"

# Cleanup
docker rm -f pg-test

Managed Alternatives​

If you're running PostgreSQL outside of Docker (e.g., AWS RDS, Supabase, Neon), use your provider's built-in backup and point-in-time recovery features instead.

Retention Suggestions​

EnvironmentFrequencyRetention
DevelopmentManualAs needed
StagingDaily7 days
ProductionDaily30 days daily, 12 months weekly

Store production backups off-site (e.g., S3, GCS, or another host) for disaster recovery.