Skip to content

Deploy: Fly.io

Oleg Ivanchenko edited this page Oct 18, 2026 · 6 revisions

Deploy: Fly.io

This guide covers deploying oCMS to Fly.io, including demo mode for public demos.

Prerequisites

  1. Fly.io CLI installed
  2. Fly.io account (free tier is sufficient)

Quick Start

# Login to Fly.io
fly auth login

# Launch the app (first time only)
fly launch --no-deploy --copy-config

# Create persistent volume for database
fly volumes create ocms_data --size 1 --region fra

# Set required secrets
fly secrets set OCMS_SESSION_SECRET="$(openssl rand -base64 32)" --app ocms-demo

# Deploy
fly deploy

Fly deploys with the project Dockerfile. The image build generates web/static/dist from source, so committed dist files and GitHub release archives are not required.

Configuration

The fly.toml configures:

  • Region: fra (Frankfurt, customizable)
  • Stop timeout: kill_timeout = 60, so the app can drain requests (up to 30 seconds) and shut down before Fly stops it
  • VM: shared-cpu-1x, 256MB RAM
  • Volume: 1GB persistent storage at /app/data
  • Health checks: Liveness (/health/live) and readiness (/health/ready)
  • Auto-scaling: Stops when idle, auto-starts on HTTP requests

Environment variables in fly.toml (excerpt):

[env]
  OCMS_ENV = 'production'
  OCMS_DO_SEED = 'true'
  OCMS_DEMO_MODE = 'true'
  OCMS_DB_PATH = '/app/data/ocms.db'
  OCMS_UPLOADS_DIR = '/app/data/uploads'

The file also sets OCMS_ACTIVE_THEME (developer), OCMS_SITE_URL, and switches off the production OCMS_REQUIRE_* checks that need configuration the demo does not have (trusted proxies, API CIDRs, form captcha and so on). It sets OCMS_MIGRATOR_ALLOWED_DB_HOSTS to the unresolvable db.invalid to satisfy the Migrator startup audit.

Secrets (set via fly secrets set):

Variable Required Description
OCMS_SESSION_SECRET Yes Min 32 bytes; keys CSRF protection (sessions are stored server-side)

Demo Mode

When OCMS_DEMO_MODE=true with OCMS_DO_SEED=true, oCMS seeds sample content, makes content read-only and blocks sensitive actions. See Module: Demo Mode for details.

Demo Content

Content Count
Users 2 (admin + editor)
Categories 4
Tags 7
Pages 10
Media 10 placeholder images
Menu items 7

Demo Credentials

Role Email Password
Admin demo@example.com Random, regenerated on every startup; shown in the informer bar at the top of the site
Editor editor@example.com demo1234demo

The seeded default admin (admin@example.com) is also rotated to a random password at startup in demo mode.

Demo Reset

In demo mode the application resets itself: the demo_reset scheduler job runs daily at 01:00 UTC, deleting the database and uploads and sending SIGTERM so the machine restarts and re-seeds. A startup check runs the same reset when more than 24 hours have passed since the last one. To reset by hand:

# Option 1: Run reset script
fly ssh console -C "/app/scripts/reset-demo.sh"
fly machines restart -a ocms-demo

# Option 2: Delete database and restart
fly ssh console -C "rm /app/data/ocms.db*" -a ocms-demo
fly machines restart -a ocms-demo

Deployment Scripts

# Standard deployment
./.fly/scripts/deploy.sh

# Deploy with database reset
./.fly/scripts/deploy.sh --reset

Monitoring

# View live logs
fly logs

# Check health
curl https://ocms-demo.fly.dev/health

# SSH into the container
fly ssh console

Cost (Free Tier)

Resource Usage Free Tier Limit
VM shared-cpu-1x, 256MB 3 VMs
Volume 1GB 3GB total
Bandwidth ~10-50GB/month 160GB/month

Docker Alternative

Build and run locally without Fly.io:

docker build -t ocms-demo .
# --stop-timeout 60 lets the app drain requests (up to 30 seconds) and shut down on docker stop
# The image defaults to OCMS_ENV=production, which refuses to start without
# OCMS_TRUSTED_PROXIES and OCMS_API_ALLOWED_CIDRS; use development for a local try-out
docker run -p 8080:8080 --stop-timeout 60 \
  -e OCMS_SESSION_SECRET="$(openssl rand -base64 32)" \
  -e OCMS_ENV=development \
  -e OCMS_DO_SEED=true \
  -e OCMS_DEMO_MODE=true \
  -v ocms_data:/app/data \
  ocms-demo

See Docker for more Docker configuration options.

Clone this wiki locally