Production-ready Docker deployment template for Strapi V5, optimized for Cloudflare Containers with custom subdomain support.
- QUICKSTART.md - Deploy in ~30 minutes
- DEPLOYMENT.md - Complete deployment guide with troubleshooting
- Multi-stage Docker build for optimized production images
- PostgreSQL database with Docker Compose
- Health checks and security best practices
- Non-root user execution
- Cloudflare Containers ready
- Environment-based configuration
- Volume management for data persistence
- Docker and Docker Compose installed
- Node.js 22+ (for local development)
- Cloudflare account (for Cloudflare deployment)
- Wrangler CLI (for Cloudflare deployment)
First, create a new Strapi V5 project:
npx create-strapi@latest my-strapi-project
cd my-strapi-projectCopy all files from this template to your Strapi project:
cp /path/to/template/* /path/to/my-strapi-project/This template includes environment configurations for different scenarios:
.env.example- Complete reference with all options.env.local.example- Local development (SQLite, debug mode).env.production.example- Production deployment (Cloudflare)
For Local Development:
cp .env.local.example .env
# Edit .env and set your subdomain/domainFor Production:
cp .env.production.example .env.production
# Edit and configure for your Cloudflare setupGenerate Secure Secrets:
Use the included script to generate all required secrets:
./scripts/generate-secrets.shOr manually generate them:
# Generate individual secrets
openssl rand -base64 32
# Run 4 times for APP_KEYS and comma-separate them
openssl rand -base64 32Important: Update these values in your .env file:
PUBLIC_URL- Set to your subdomain (e.g.,https://api.yourdomain.com)CORS_ORIGINS- Add your frontend domains- Database credentials
- All generated secrets
Start the application with PostgreSQL:
docker-compose up -dAccess Strapi at: http://localhost:1337
View logs:
docker-compose logs -f strapiStop services:
docker-compose down- Install Wrangler CLI:
npm install -g wrangler- Authenticate with Cloudflare:
wrangler login- Ensure Docker is running locally:
docker infoCloudflare Containers requires special database configuration. You have two options:
Option A: Use Cloudflare D1 (SQLite)
Update your config/database.ts to use SQLite:
export default {
connection: {
client: 'better-sqlite3',
connection: {
filename: path.join(__dirname, '..', '..', '.tmp/data.db'),
},
useNullAsDefault: true,
},
};Option B: Use External PostgreSQL
Use a managed PostgreSQL service (Neon, Supabase, etc.) and configure the connection in your environment variables.
Set sensitive environment variables as secrets:
wrangler secret put JWT_SECRET
wrangler secret put ADMIN_JWT_SECRET
wrangler secret put API_TOKEN_SALT
wrangler secret put TRANSFER_TOKEN_SALT
wrangler secret put APP_KEYSFor external database:
wrangler secret put DATABASE_HOST
wrangler secret put DATABASE_PASSWORDwrangler deployThis will:
- Build your Docker image using the Dockerfile
- Push the image to Cloudflare's Container Registry
- Deploy your Worker
- Configure Durable Objects
After first deployment, wait several minutes for the container to be ready.
Check container status:
wrangler containers listView deployed images:
wrangler containers images list- Architecture: Images must support
linux/amd64 - Storage: Consider using Cloudflare R2 for file uploads
- Database: Use Cloudflare D1 or external managed database
- Secrets: Never commit secrets to version control
- Cold Starts: First requests may be slower as containers spin up
- Max Instances: Configured in
wrangler.toml(default: 10)
.
├── Dockerfile # Multi-stage production Dockerfile
├── docker-compose.yml # Local development orchestration
├── wrangler.toml # Cloudflare Containers configuration
├── .dockerignore # Docker build exclusions
├── .gitignore # Git exclusions
├── .env.example # Complete environment variables reference
├── .env.local.example # Local development environment
├── .env.production.example # Production environment template
├── scripts/
│ └── generate-secrets.sh # Secret generation utility
├── README.md # This file
├── QUICKSTART.md # Quick deployment guide (~30 min)
└── DEPLOYMENT.md # Complete deployment guide
| Variable | Description | Example |
|---|---|---|
NODE_ENV |
Application environment | production |
DATABASE_CLIENT |
Database type | postgres or better-sqlite3 |
DATABASE_HOST |
Database host | postgres |
DATABASE_PORT |
Database port | 5432 |
DATABASE_NAME |
Database name | strapi |
DATABASE_USERNAME |
Database user | strapi |
DATABASE_PASSWORD |
Database password | secure_password |
JWT_SECRET |
JWT signing key | Generate with openssl |
ADMIN_JWT_SECRET |
Admin JWT key | Generate with openssl |
APP_KEYS |
Session keys (comma-separated) | Generate 4 keys |
API_TOKEN_SALT |
API token salt | Generate with openssl |
TRANSFER_TOKEN_SALT |
Transfer token salt | Generate with openssl |
| Variable | Description | Example |
|---|---|---|
PUBLIC_URL |
Full public URL with subdomain | https://api.yourdomain.com |
CORS_ORIGINS |
Allowed CORS origins (comma-separated) | https://yourdomain.com,https://www.yourdomain.com |
CF_ACCOUNT_ID |
Cloudflare Account ID | Found in dashboard |
CF_ZONE_ID |
Cloudflare Zone ID for your domain | Found in domain settings |
CF_API_TOKEN |
Cloudflare API token | Create with Workers/DNS permissions |
| Variable | Description | Default |
|---|---|---|
HOST |
Server host | 0.0.0.0 |
PORT |
Server port | 1337 |
ADMIN_PATH |
Admin panel path | /admin |
RATE_LIMIT_ENABLED |
Enable rate limiting | true |
MAX_FILE_SIZE |
Max upload size in bytes | 104857600 (100MB) |
LOG_LEVEL |
Logging level | info |
See the example files for complete documentation:
.env.example- All available options with detailed comments.env.local.example- Optimized for local development.env.production.example- Production-ready with security best practices
docker build -t strapi-v5:latest .docker run -p 1337:1337 --env-file .env strapi-v5:latestdocker logs -f strapi-appdocker exec -it strapi-app shIf Strapi can't connect to the database:
- Ensure PostgreSQL is running:
docker-compose ps - Check database logs:
docker-compose logs postgres - Verify environment variables in
.env
If Docker build fails:
- Ensure all dependencies are in
package.json - Check Node.js version compatibility
- Verify build logs for missing packages
- Docker not running: Run
docker infoto verify - Authentication failed: Run
wrangler loginagain - Image architecture: Ensure you're building for
linux/amd64 - Container not ready: Wait several minutes after first deployment
If health checks fail:
- Check if Strapi is listening on port 1337
- Verify the health endpoint is accessible
- Check container logs for errors
- Never commit
.envfiles to version control - Use strong, randomly generated secrets
- Run containers as non-root user (already configured)
- Keep dependencies updated
- Use managed database services for production
- Enable SSL/TLS for database connections
- Regularly backup your database
- Use multi-stage builds (already implemented)
- Minimize image size by excluding unnecessary files
- Use production dependencies only in final image
- Configure appropriate resource limits
- Enable caching layers in Cloudflare
- Use CDN for static assets
docker exec strapi-db pg_dump -U strapi strapi > backup.sqldocker exec -i strapi-db psql -U strapi strapi < backup.sql- Update
package.jsonwith new version - Rebuild Docker image
- Test locally with docker-compose
- Deploy to Cloudflare
docker-compose down
docker-compose build --no-cache
docker-compose up -d- Strapi Documentation: https://docs.strapi.io
- Strapi GitHub: https://github.com/strapi/strapi
- Cloudflare Containers Docs: https://developers.cloudflare.com/containers/
- Docker Documentation: https://docs.docker.com
This template is provided as-is for use with Strapi V5 projects.