Moving from a local Frappe development environment to a live Docker production setup can feel daunting. In production, you aren’t simply running bench get-app inside a live container—ephemeral containers mean any changes made inside an active container will disappear upon restart!
To deploy custom apps in production, your custom apps must be baked directly into the custom Docker image and orchestrated via Docker Compose alongside Redis, MariaDB, and Traefik reverse proxies.
Here is the straightforward, production-grade guide to deploying custom Frappe apps with Docker.
The Production Docker Workflow
Unlike monolithic bench installs, production Docker deployments use separated microservice containers:
backend(Gunicorn Python WSGI)frontend(Nginx serving static assets and proxying requests)websocket(Node.js socketio service)queue-default,queue-short,queue-long(Background Celery workers)scheduler(Cron tasks)
All these services share the same custom image containing your app code and static assets.
Step 1: Bake Your Custom App into the Production Image
In your deployment repository:
- Create a
compose.yamland reference your custom Dockerfile or pre-built image tag. - Ensure your custom image includes both your custom app and official apps like ERPNext or HRMS.
- Build and tag your image locally or via GitHub Actions:
docker compose build
Step 2: Running the Database Migrations
Once your containers are started:
docker compose up -d
You must run migrations to create the custom DocTypes, fields, and permissions in your MariaDB database:
docker compose exec backend bench --site yourdomain.com install-app your_custom_app
docker compose exec backend bench --site yourdomain.com migrate
Step 3: Rebuilding Assets for Production Nginx
If your custom app includes client scripts, custom web views, or bundled Vue/React bundles:
docker compose exec backend bench build --app your_custom_app
docker compose exec backend bench --site yourdomain.com clear-cache
Restart the frontend and backend services to ensure Gunicorn workers reload python bytecode:
docker compose restart backend frontend websocket
Key Production Gotchas
- Volume Persistence: Never store custom app code in ephemeral root directories. Persistent files, logs, and private/public uploads must be mounted to named Docker volumes (
sites). - Database Backups: Always run
bench --site yourdomain.com backup --with-filesbefore executing migrations on live production databases. - Environment Variables: Keep secret database passwords and encryption keys in
.envfiles with restricted600file permissions.
Summary
Containerizing Frappe apps for production ensures seamless horizontal scaling, instant rollbacks, and zero host dependency conflicts.
Looking for dedicated Frappe & ERPNext cloud deployment, server migration, or custom ERP development? Connect with the engineers at Klickspell.