Deployment#
Architecture#
The production stack consists of:
PostgreSQL — primary database
Redis — Celery broker and Flask cache backend
Gunicorn — WSGI server, reverse-proxied behind nginx
Celery worker — processes background tasks on the
opsqueueCelery beat — scheduled task dispatcher
nginx — reverse proxy and static file server
CDN — edge caching for the NAS catalog endpoint
Object Storage — S3-compatible storage (e.g. Fastly Object Storage) for SPK packages
Configuration#
The SPKREPO_CONFIG environment variable points to a Python config file with production settings:
export SPKREPO_CONFIG=/etc/spkrepo/production.cfg
A minimal production config file looks like this:
SECRET_KEY = "replace-with-a-long-random-string"
SQLALCHEMY_DATABASE_URI = "postgresql+psycopg2://spkrepo:password@db/spkrepo"
CELERY_BROKER_URL = "redis://redis:6379/1"
# S3-compatible object storage
OBJECT_STORAGE_PACKAGES_ENDPOINT = "https://s3.example.com"
OBJECT_STORAGE_PACKAGES_REGION = "us-east-1"
OBJECT_STORAGE_PACKAGES_BUCKET = "spkrepo-packages"
OBJECT_STORAGE_PACKAGES_ACCESS_KEY = "your-access-key"
OBJECT_STORAGE_PACKAGES_SECRET_KEY = "your-secret-key"
CDN_PURGE_TOKEN = "your-cdn-api-token"
PACKAGES_CDN_HOST = "packages.example.com"
# GPG signing
GNUPG_PATH = "/path/to/gnupg-home"
GNUPG_FINGERPRINT = "ABCDEF1234567890"
GNUPG_TIMESTAMP_URL = "http://timestamp.synology.com/timestamp.php"
Gunicorn#
Run Gunicorn with enough workers to handle concurrent NAS catalog requests:
gunicorn "spkrepo:create_app()" \
--workers 4 \
--bind 0.0.0.0:8000 \
--access-logfile - \
--error-logfile -
nginx#
A minimal nginx reverse proxy configuration:
server {
listen 80;
server_name packages.example.com;
client_max_body_size 200M; # allow large SPK uploads
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Celery workers#
Start the worker and beat scheduler:
# Worker (processes background tasks)
uv run celery -A spkrepo.celery worker -Q ops --loglevel=info
# Beat (dispatches scheduled tasks)
uv run celery -A spkrepo.celery beat --loglevel=info
Database migrations#
Always run migrations before starting the application after an upgrade:
uv run flask db upgrade
See Database Migrations for full migration reference.