Skip to content

Hosting Platforms

Choose the database and migration model before selecting a hosting service. For multiple Roster instances, follow the High Availability architecture. Every production deployment needs HTTPS, stable secrets, health checks, centralized logs, and appropriate backup coverage.

SQLite requires one Roster instance and a durable block volume mounted with local filesystem semantics at /data. SQLite’s WAL mode does not support network filesystems such as NFS, EFS, or SMB. Use PostgreSQL when a hosting service provides only ephemeral or network-mounted storage. Follow the SQLite backup procedure for online and hosted-container backups.

Choose the runtime and database together:

ServiceSQLite supportPostgreSQL support
ECS on FargateNo for ECS services; task EBS volumes are not retainedEnterprise; required for production Roster
App Runner (existing customers)No; its container filesystem is ephemeralEnterprise; required for production Roster
EKSOne pod with an EBS-backed block PersistentVolumeEnterprise; required for multiple Roster pods

App Runner is stateless and uses ephemeral storage, so do not deploy SQLite there. AWS closed App Runner to new customers; this guidance applies only to existing App Runner users. ECS deletes EBS volumes attached to service-managed tasks when those tasks terminate, so Fargate services must use PostgreSQL for persistent Roster data. For EKS SQLite, run exactly one Roster pod with an EBS-backed block PersistentVolume and do not use EFS. Store ROSTER_AUTH_SECRET, provider credentials, license values, and encryption keys in AWS Secrets Manager or SSM Parameter Store. Use Amazon RDS or another managed PostgreSQL 17/18 service for Fargate, App Runner, and high-availability deployments.

ServiceSQLite supportPostgreSQL support
Cloud RunNo; available volume types do not meet SQLite locking requirementsEnterprise; required for production Roster
GKEOne pod with a Persistent Disk-backed PersistentVolume at /dataEnterprise; required for multiple Roster pods

Cloud Run NFS volumes do not support file locking, and Cloud Storage FUSE is not fully POSIX-compliant and does not provide file locking. Use Cloud SQL or another managed PostgreSQL 17/18 service with Cloud Run. For GKE SQLite, run exactly one Roster pod and use a block-backed Persistent Disk, not NFS or Cloud Storage FUSE. Store secrets in Secret Manager and set ROSTER_AUTH_URL to the final public HTTPS origin.

ServiceSQLite supportPostgreSQL support
Azure Container AppsNo; persistent mounts use Azure Files SMB or NFSEnterprise; required for production Roster
Azure App Service for ContainersNo; storage mounts are not supported for local databasesEnterprise; required for production Roster
AKSOne pod with a Managed Disk-backed volume at /dataEnterprise; required for multiple Roster pods

Azure Container Apps persistent volumes use Azure Files SMB or NFS. Azure App Service explicitly says not to use storage mounts for SQLite or applications that require file locks. Use Azure Database for PostgreSQL or another managed PostgreSQL 17/18 service with those managed web runtimes. For AKS SQLite, run exactly one Roster pod on a Managed Disk-backed volume. Store secrets in Key Vault and set ROSTER_AUTH_URL to the final public HTTPS origin.

Fly.io can host either Roster deployment shape. Choose the topology before creating the app:

  • Standalone SQLite: run exactly one Machine and mount a Fly Volume at /data. Keep stable runtime secrets, back up the volume, and do not scale the app horizontally.
  • PostgreSQL HA: run two or more stateless Machines without a Fly Volume, connect them to PostgreSQL 17 or 18 writer and reader endpoints, and follow the High Availability guide.

For standalone SQLite, create the persistent volume and deploy one Machine:

Save a fly.toml for the app before deploying. Replace the example app name and region with the values selected for the deployment:

app = "your-roster-app"
primary_region = "yyz"
[build]
image = "advantys/roster:1.2.0"
[env]
HOST = "0.0.0.0"
PORT = "3000"
ROSTER_DATA_DIR = "/data"
[[mounts]]
source = "roster_data"
destination = "/data"
[http_service]
internal_port = 3000
force_https = true
auto_stop_machines = "stop"
auto_start_machines = true
min_machines_running = 0

Then create the app and volume, save the printed bootstrap password, configure the secrets, and deploy exactly one Machine:

Terminal window
app_name=your-roster-app
region=yyz
bootstrap_admin_password='Roster-1!'"$(openssl rand -hex 24)"
printf 'Save this bootstrap admin password now: %s\n' "$bootstrap_admin_password"
flyctl apps create "$app_name"
flyctl volumes create roster_data -a "$app_name" --region "$region" --size 1
flyctl secrets set -a "$app_name" \
"ROSTER_AUTH_URL=https://${app_name}.fly.dev" \
"ROSTER_AUTH_SECRET=$(openssl rand -base64 48)" \
"ROSTER_BOOTSTRAP_ADMIN_PASSWORD=$bootstrap_admin_password" \
"ROSTER_PROVIDER_SECRET_ENCRYPTION_KEY=$(openssl rand -base64 48)"
flyctl deploy -a "$app_name" --ha=false
flyctl scale count 1 -a "$app_name"
unset bootstrap_admin_password

Confirm https://your-roster-app.fly.dev/health/ready returns a ready response, then sign in at the app URL. The bootstrap password is needed only for a new or reset database. After the administrator exists and login succeeds, remove it:

Terminal window
flyctl secrets unset ROSTER_BOOTSTRAP_ADMIN_PASSWORD -a "$app_name"

Keep the auth secret and provider-encryption key configured for every subsequent deploy.

This standalone shape can experience downtime when the Machine, its host, or its local volume is unavailable. Keep tested off-host backups; do not create a second SQLite Machine because Fly Volumes do not replicate data between Machines.

For PostgreSQL HA, provision and test the database, read replica, migration job, shared connector storage, secrets, and failover procedure before scaling Roster. Fly secrets reach every Machine in an app, so keep the privileged database URL in a separate private migrator app. Never add the migrator URL to the runtime app. Roster does not provide an automatic SQLite-to-PostgreSQL data migration.

For Kubernetes-based platforms, standalone SQLite uses one Roster instance and a PersistentVolumeClaim mounted at /data. High availability uses two or more Roster instances and an external PostgreSQL cluster.

Core runtime requirements:

  • one advantys/roster container per pod
  • containerPort: 3000
  • readiness on /health/ready and liveness on /health/live
  • secrets injected as environment variables
  • ingress with HTTPS and a stable public hostname

SQLite deployments additionally require a block-backed PersistentVolumeClaim mounted at /data; do not use an NFS-backed StorageClass. Roster instances in an HA deployment use ROSTER_DEPLOYMENT_MODE=ha to coordinate rate limits and workers through PostgreSQL.

Render can run the published Docker image and offers managed PostgreSQL. A persistent disk mounted at /data can support a standalone SQLite deployment when it provides local filesystem semantics, but disk-backed services cannot scale to multiple instances. Use PostgreSQL and stateless Roster instances for high availability.

Railway can deploy the Docker image with public networking, variables, volumes, and PostgreSQL services. Mount a volume with local filesystem semantics at /data for standalone SQLite. Railway does not allow service replicas with a volume attached. Use PostgreSQL and stateless Roster instances for high availability.

DigitalOcean App Platform can deploy container images and integrate with managed databases and Spaces object storage. Its local filesystem is ephemeral, so use PostgreSQL for production and S3-compatible Spaces for connector files.

Heroku deploys Docker images through the Heroku Container Registry and integrates with Heroku Postgres. Dyno filesystems are ephemeral and do not support mounted volumes. Use PostgreSQL and remote connector storage; do not use SQLite for production.

For every PostgreSQL deployment, keep the schema-owning migration credential separate from the restricted runtime credential. Use S3 or SFTP connectors when runtime instances do not share durable /data storage. For HA, configure the writer endpoint with ROSTER_DATABASE_URL and read-only endpoints with ROSTER_DATABASE_REPLICA_URLS.

Return to the Deployment Overview for the production checklist and post-deployment steps.