$npx -y skills add Impertio-Studio/Frappe_Claude_Skill_Package --skill frappe-ops-deploymentUse when deploying Frappe/ERPNext to production, configuring Nginx or Supervisor, setting up Docker, enabling SSL, or hardening security. Prevents insecure deployments, missing reverse proxy config, and broken process management. Covers production setup, Nginx configuration, Supe
| 1 | # Production Deployment |
| 2 | |
| 3 | Deploy Frappe/ERPNext to production using either traditional (bench + Nginx + Supervisor) or Docker (frappe_docker + Compose). Frappe officially recommends Docker for new deployments. |
| 4 | |
| 5 | ## Quick Reference |
| 6 | |
| 7 | ```bash |
| 8 | # Traditional production setup (one command) |
| 9 | sudo bench setup production [frappe-user] |
| 10 | |
| 11 | # What it configures: |
| 12 | # 1. Supervisor — process management (gunicorn, workers, Redis, socketio) |
| 13 | # 2. Nginx — reverse proxy, static files, websocket proxy |
| 14 | # 3. Sudoers — allows frappe-user to restart services |
| 15 | |
| 16 | # Individual setup commands |
| 17 | bench setup supervisor # Generate supervisor config |
| 18 | bench setup nginx # Generate nginx config |
| 19 | bench setup sudoers $(whoami) # Allow service restarts without password |
| 20 | |
| 21 | # Symlink configs into system directories |
| 22 | sudo ln -s $(pwd)/config/supervisor.conf /etc/supervisor/conf.d/frappe-bench.conf |
| 23 | sudo ln -s $(pwd)/config/nginx.conf /etc/nginx/conf.d/frappe-bench.conf |
| 24 | |
| 25 | # SSL setup |
| 26 | sudo -H bench setup lets-encrypt [site-name] |
| 27 | sudo -H bench setup lets-encrypt [site-name] --custom-domain [domain] |
| 28 | |
| 29 | # DNS multitenancy (multiple sites on port 80/443) |
| 30 | bench config dns_multitenant on |
| 31 | bench setup nginx |
| 32 | sudo service nginx reload |
| 33 | ``` |
| 34 | |
| 35 | --- |
| 36 | |
| 37 | ## Deployment Decision Tree |
| 38 | |
| 39 | ``` |
| 40 | Which deployment method? |
| 41 | | |
| 42 | +-- New server, minimal ops experience? |
| 43 | | +-- Docker (frappe_docker) — recommended by Frappe |
| 44 | | |
| 45 | +-- Existing server with bench already installed? |
| 46 | | +-- Traditional (bench setup production) |
| 47 | | |
| 48 | +-- Need custom Frappe apps or complex build? |
| 49 | | +-- Docker with custom image build |
| 50 | | |
| 51 | +-- Cloud hosting (AWS/GCP/Azure)? |
| 52 | | +-- Docker on VM or Kubernetes |
| 53 | | +-- OR Frappe Cloud (managed) |
| 54 | | |
| 55 | +-- Single site or multi-site? |
| 56 | | +-- Single site: standard setup |
| 57 | | +-- Multi-site: DNS multitenancy required |
| 58 | ``` |
| 59 | |
| 60 | --- |
| 61 | |
| 62 | ## Traditional Deployment |
| 63 | |
| 64 | ### Process Architecture |
| 65 | |
| 66 | ``` |
| 67 | Internet → Nginx (port 80/443) |
| 68 | | |
| 69 | +-- Static files served directly |
| 70 | +-- /api, /app → Gunicorn (port 8000) |
| 71 | +-- /socket.io → Node.js socketio (port 9000) |
| 72 | |
| 73 | Supervisor manages: |
| 74 | - frappe-bench-web (gunicorn) |
| 75 | - frappe-bench-socketio (node) |
| 76 | - frappe-bench-worker-short |
| 77 | - frappe-bench-worker-default |
| 78 | - frappe-bench-worker-long |
| 79 | - frappe-bench-redis-cache |
| 80 | - frappe-bench-redis-queue |
| 81 | - frappe-bench-schedule (scheduler) |
| 82 | ``` |
| 83 | |
| 84 | ### Step-by-Step Setup |
| 85 | |
| 86 | ```bash |
| 87 | # 1. Install bench (as non-root user) |
| 88 | sudo pip3 install frappe-bench |
| 89 | bench init frappe-bench --frappe-branch version-15 |
| 90 | cd frappe-bench |
| 91 | |
| 92 | # 2. Create site |
| 93 | bench new-site mysite.example.com |
| 94 | bench --site mysite.example.com install-app erpnext |
| 95 | |
| 96 | # 3. Production setup (configures nginx + supervisor + sudoers) |
| 97 | sudo bench setup production $(whoami) |
| 98 | |
| 99 | # 4. Verify processes are running |
| 100 | sudo supervisorctl status |
| 101 | |
| 102 | # 5. Verify nginx config |
| 103 | sudo nginx -t && sudo systemctl reload nginx |
| 104 | ``` |
| 105 | |
| 106 | ### Nginx Configuration |
| 107 | |
| 108 | `bench setup nginx` generates `config/nginx.conf` with: |
| 109 | - Server block per site (DNS multitenancy) |
| 110 | - Proxy to gunicorn on port 8000 |
| 111 | - WebSocket proxy to socketio on port 9000 |
| 112 | - Static file serving from `sites/` directory |
| 113 | - Client max body size (default varies by version) |
| 114 | |
| 115 | **ALWAYS disable default nginx site** to avoid port 80 conflicts: |
| 116 | ```bash |
| 117 | sudo rm /etc/nginx/sites-enabled/default |
| 118 | # OR disable: sudo mv /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf.bak |
| 119 | ``` |
| 120 | |
| 121 | ### Supervisor Configuration |
| 122 | |
| 123 | `bench setup supervisor` generates `config/supervisor.conf` with: |
| 124 | - `--skip-redis` flag to skip Redis if managed externally |
| 125 | |
| 126 | For CentOS/RHEL: use `.ini` extension instead of `.conf` for supervisor configs. |
| 127 | |
| 128 | --- |
| 129 | |
| 130 | ## SSL / HTTPS |
| 131 | |
| 132 | ### Let's Encrypt (Recommended) |
| 133 | |
| 134 | ```bash |
| 135 | # Automated setup with cron renewal |
| 136 | sudo -H bench setup lets-encrypt mysite.example.com |
| 137 | |
| 138 | # For custom domain (site name differs from domain) |
| 139 | sudo -H bench setup lets-encrypt mysite.example.com --custom-domain www.example.com |
| 140 | |
| 141 | # Manual renewal |
| 142 | sudo bench renew-lets-encrypt |
| 143 | ``` |
| 144 | |
| 145 | **Prerequisites**: |
| 146 | - DNS multitenancy enabled (`bench config dns_multitenant on`) |
| 147 | - Domain resolves to server IP |
| 148 | - Port 80 open for ACME challenge |
| 149 | - Root/sudo access |
| 150 | |
| 151 | **Certificate locations**: `/etc/letsencrypt/live/example.com/` |
| 152 | - `fullchain.pem` — certificate + chain |
| 153 | - `privkey.pem` — private key |
| 154 | |
| 155 | Cert |