frappe_docker/production/README.md
duthink f167e83bda Add production environment configuration and deployment scripts for ERPNext
- Created example environment files for MariaDB and production settings.
- Implemented backup script for ERPNext sites with options for file inclusion, compression, and encryption.
- Developed site creation script to streamline ERPNext site setup with admin password handling.
- Added deployment script to manage the deployment of ERPNext, MariaDB, and Traefik services.
- Introduced log viewing script for monitoring ERPNext services.
- Implemented stop script to manage stopping of ERPNext and its dependencies.
- Added validation script to check environment configuration for common issues and security best practices.
2025-11-13 15:32:30 +05:30

1637 lines
No EOL
41 KiB
Markdown

# Production ERPNext Deployment
Complete guide for deploying ERPNext in production using Docker Compose with Traefik and Let's Encrypt SSL.
---
## Overview
This guide provides everything needed to deploy a production-ready ERPNext instance using Docker Compose. The setup includes:
- **ERPNext v15.82.1** - ERP application
- **Frappe Framework v15.82.1** - Application framework
- **Traefik v2.11** - Reverse proxy with automatic Let's Encrypt SSL
- **MariaDB 11.8** - Database server
- **Redis** - Cache, queue, and socketio
### Architecture
Three separate Docker Compose projects work together:
1. **Traefik** - Reverse proxy, SSL certificates, load balancing
2. **MariaDB** - Shared database for all ERPNext sites
3. **ERPNext** - Application containers (backend, frontend, workers, scheduler)
### Repository Structure
```
erp-is/
├── production/ # Production deployment (this directory)
│ ├── README.md # This guide
│ ├── *.env.example # Configuration templates
│ ├── customizations/ # Brand customizations (CSS, favicon, logo)
│ │ ├── custom.css
│ │ ├── favicon.ico
│ │ └── logo.png
│ ├── scripts/ # Automation scripts
│ │ ├── deploy.sh # Main deployment (includes setup/regenerate)
│ │ ├── create-site.sh # Create new site
│ │ ├── backup-site.sh # Backup automation
│ │ ├── validate-env.sh # Config validation
│ │ ├── logs.sh # Log viewer
│ │ └── stop.sh # Stop services
│ └── production.yaml # Generated compose file (do not edit)
├── overrides/ # Upstream compose overlays
├── docs/ # Upstream documentation
└── compose.yaml # Upstream base configuration
```
**Important**: This is a fork of [frappe/frappe_docker](https://github.com/frappe/frappe_docker):
- **Custom files** (in `production/`) are maintained in this fork
- **Infrastructure files** (compose.yaml, overrides/) track upstream
- **Container images** are pulled from official Frappe Docker registry
---
## Table of Contents
- [Quick Start](#quick-start)
- [Prerequisites](#prerequisites)
- [Pre-Deployment Checklist](#pre-deployment-checklist)
- [Environment Configuration](#environment-configuration)
- [Deployment](#deployment)
- [Script Usage Guide](#script-usage-guide)
- [Branding Customization](#branding-customization)
- [Common Operations](#common-operations)
- [Update Procedures](#update-procedures)
- [Troubleshooting](#troubleshooting)
- [Security](#security)
- [Architecture Explained](#architecture-explained)
- [Script Optimizations](#script-optimizations)
---
## Quick Start
**For experienced users:**
```bash
# 1. Generate secure passwords
openssl rand -base64 32 # DB password
openssl rand -base64 24 # Admin password
htpasswd -nB admin # Traefik dashboard password
# 2. Configure environment (interactive setup)
./scripts/deploy.sh --setup # Creates *.env from templates
# Edit all three files with your values
# 3. Validate configuration
./scripts/validate-env.sh # Checks for errors before deploy
# 4. Deploy all services
./scripts/deploy.sh
# 5. Create your first site
./scripts/create-site.sh erp.example.com
# 6. View logs
./scripts/logs.sh # Interactive service selection
```
**First time?** Follow the complete checklist below.
---
## Prerequisites
### System Requirements
- **OS**: Ubuntu 20.04/22.04 LTS or Debian 11/12
- **CPU**: 2+ cores (4+ recommended)
- **RAM**: 4GB minimum (8GB+ recommended)
- **Disk**: 50GB+ SSD storage
- **Network**: Public IP with ports 80 and 443 open
- **Domain**: DNS pointing to your server
### Software Requirements
```bash
# Docker Engine 20.10+
docker --version
# Docker Compose v2
docker compose version
```
---
## Pre-Deployment Checklist
### ✅ 1. Verify System
```bash
# Check OS version
lsb_release -a
# Check available RAM
free -h
# Check disk space
df -h
# Check server is accessible
ping -c 3 $(hostname -I | awk '{print $1}')
```
### ✅ 2. Configure DNS
```bash
# Verify DNS propagation (wait 24-48h after DNS change)
dig +short erp.example.com
# Should return your server's public IP
```
### ✅ 3. Configure Firewall
```bash
# Allow required ports
sudo ufw allow 22/tcp # SSH
sudo ufw allow 80/tcp # HTTP
sudo ufw allow 443/tcp # HTTPS
sudo ufw enable
# Verify
sudo ufw status
```
### ✅ 4. Install Docker
```bash
# Install Docker Engine
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
# Add user to docker group
sudo usermod -aG docker $USER
# Logout and login again, then test
docker ps
```
### ✅ 5. Validate Environment
```bash
# Run validation script (checks all config files)
./scripts/validate-env.sh
# Should pass all checks before proceeding
```
---
## Script Usage Guide
All scripts have been optimized for efficiency and include comprehensive help. Every script supports `-h` or `--help`.
### 📋 Script Overview
| Script | Purpose | Key Features |
|--------|---------|--------------|
| **deploy.sh** | Main deployment | `--setup`, `--regenerate`, validation |
| **create-site.sh** | Create ERPNext site | Interactive prompts, validation |
| **backup-site.sh** | Advanced backups | Encryption, auto-copy, cleanup |
| **logs.sh** | View service logs | Interactive menu, service selection |
| **stop.sh** | Stop services | Selective stopping, `--all` option |
| **validate-env.sh** | Config validation | Password strength, cross-validation |
### 🚀 Deploy Script
```bash
# Get help
./scripts/deploy.sh --help
# Setup environment files from templates
./scripts/deploy.sh --setup
# Validate and deploy all services
./scripts/deploy.sh
# Regenerate production.yaml only (no deploy)
./scripts/deploy.sh --regenerate
```
### 🏗️ Create Site Script
```bash
# Get help
./scripts/create-site.sh --help
# Interactive mode
./scripts/create-site.sh
# Direct usage
./scripts/create-site.sh erp.example.com
./scripts/create-site.sh erp.example.com MySecurePass123
```
### 💾 Backup Script (Advanced)
```bash
# Get help
./scripts/backup-site.sh --help
# Basic backup
./scripts/backup-site.sh erp.example.com
# Advanced backup with files, auto-copy, cleanup
./scripts/backup-site.sh erp.example.com --with-files --auto-copy --cleanup-old
# Encrypted backup
BACKUP_PASSPHRASE='your-secret' ./scripts/backup-site.sh erp.example.com --encrypt --auto-copy
# Automated (environment variables)
AUTO_COPY=1 CLEANUP_OLD=1 ./scripts/backup-site.sh erp.example.com
```
**Backup Features:**
- **Encryption**: GPG symmetric encryption with AES256
- **Auto-copy**: Copy backups from container to host `./backups/`
- **Cleanup**: Remove old backups (configurable retention)
- **Validation**: Verify backup files and sizes
- **Logging**: Detailed operation logs in `/tmp/`
### 📊 Logs Script
```bash
# Get help
./scripts/logs.sh --help
# Interactive menu
./scripts/logs.sh
# Direct service access
./scripts/logs.sh 1 # Backend logs
./scripts/logs.sh backend # Same as above
./scripts/logs.sh frontend # Nginx logs
./scripts/logs.sh all # All services
```
**Available Services:**
1. **backend** - Gunicorn application server
2. **frontend** - Nginx reverse proxy
3. **websocket** - Socket.io for real-time features
4. **queue-short** - Short-running background jobs
5. **queue-long** - Long-running background jobs
6. **scheduler** - Cron-like background scheduler
7. **all** - All services combined
### 🛑 Stop Script
```bash
# Get help
./scripts/stop.sh --help
# Interactive mode (asks about MariaDB/Traefik)
./scripts/stop.sh
# Stop everything without prompts
./scripts/stop.sh --all
```
### ✅ Validation Script
```bash
# Get help
./scripts/validate-env.sh --help
# Validate all environment files
./scripts/validate-env.sh
```
**Validation Checks:**
- Required variables present
- Password strength (16+ characters recommended)
- Email format validation
- Cross-file password matching
- Placeholder detection
- Weak password detection
---
## Environment Configuration
### Three Environment Files Required
1. **`production/production.env`** - ERPNext application config
2. **`production/mariadb.env`** - Database config
3. **`production/traefik.env`** - Reverse proxy config
### Step-by-Step Configuration
#### 1. Generate Passwords
```bash
# Database password (use in production.env AND mariadb.env)
openssl rand -base64 32
# Admin password (use in production.env)
openssl rand -base64 24
# Traefik dashboard password (use in traefik.env)
htpasswd -nB admin
# Or use: https://hostingcanada.org/htpasswd-generator/
```
#### 2. Configure `production/production.env`
```bash
./scripts/deploy.sh --setup # Create from templates
nano production/production.env
```
Required values:
```env
SITES=erp.example.com # Your domain
ERPNEXT_VERSION=v15.82.1 # ERPNext version
FRAPPE_VERSION=v15.82.1 # Frappe version
DB_HOST=mariadb-database # Database host
DB_PASSWORD=<paste-generated-db-password> # From step 1
REDIS_CACHE=redis-cache:6379 # Redis cache
REDIS_QUEUE=redis-queue:6379 # Redis queue
REDIS_SOCKETIO=redis-socketio:6379 # Redis socketio
LETSENCRYPT_EMAIL=admin@example.com # For SSL certs
ADMIN_PASSWORD=<paste-generated-admin-password> # From step 1
```
#### 3. Configure `production/mariadb.env`
```bash
nano production/mariadb.env # Already created by --setup
```
Required values:
```env
DB_PASSWORD=<same-as-production.env> # MUST match production.env
```
#### 4. Configure `production/traefik.env`
```bash
nano production/traefik.env # Already created by --setup
```
Required values:
```env
TRAEFIK_DOMAIN=traefik.example.com # Dashboard subdomain
EMAIL=admin@example.com # For SSL certs
HASHED_PASSWORD=<paste-from-htpasswd> # From step 1
```
#### 5. Secure Files
```bash
chmod 600 production/*.env
# Verify they're git-ignored
git status | grep production.env
# Should return nothing
```
---
## Deployment
### Deploy Services
```bash
./scripts/deploy.sh
```
**What it does:**
1. **Validates** environment configuration (`./scripts/validate-env.sh`)
2. **Deploys Traefik** (reverse proxy + SSL)
3. **Deploys MariaDB** (database)
4. **Generates** ERPNext configuration (`production.yaml`) from:
- Base: `compose.yaml`
- Redis overlay: `overrides/compose.redis.yaml`
- Multi-bench overlay: `overrides/compose.multi-bench.yaml`
- SSL overlay: `overrides/compose.multi-bench-ssl.yaml`
5. **Deploys ERPNext** (backend, frontend, workers, scheduler)
**Expected output:**
```
[INFO] ERPNext Production Deployment
[INFO] Validating configuration...
✅ Validation Passed
[INFO] Step 1: Deploying Traefik...
✓ Traefik deployed
[INFO] Step 2: Deploying MariaDB...
✓ MariaDB deployed. Waiting 30s for initialization...
[INFO] Step 3: Generating production.yaml...
✓ Generated
[INFO] Step 4: Deploying ERPNext...
✓ ERPNext deployed
[INFO] ✓ Deployment complete!
```
### Create Site
```bash
./scripts/create-site.sh erp.example.com
```
**Wait 2-3 minutes for:**
- Database initialization
- Frappe installation
- ERPNext installation
- SSL certificate generation
### Verify Deployment
```bash
# Check all services are running
docker ps
# Check service health
docker compose -f production/production.yaml ps
# View logs
./scripts/logs.sh
# Follow logs (live)
./scripts/logs.sh -f backend
```
```
### Access Your Site
1. Open browser: `https://erp.example.com`
2. Username: `Administrator`
3. Password: Check `production/production.env` → `ADMIN_PASSWORD`
---
## Branding Customization
### Overview
Customize your ERPNext instance with your company's brand identity including favicon, logo, colors, and styling. This section provides **manual procedures** for applying branding changes.
### Step 1: Prepare Customization Assets
#### Create Directory Structure
```bash
mkdir -p production/customizations
cd production/customizations
```
#### Prepare Your Files
You'll need:
- **favicon.ico** - Browser tab icon (16x16, 32x32, 48x48 px)
- **favicon.png** - Modern browser icon (192x192 px recommended)
- **logo.png** - Company logo (any size, PNG with transparency recommended)
- **custom.css** - Brand colors and styling overrides
#### Generate Favicon from Logo (Optional)
If you have a logo and need to create favicons:
```bash
# Install ImageMagick
sudo apt install imagemagick -y
# Generate multiple sizes
convert your-logo.png -resize 16x16 favicon-16.ico
convert your-logo.png -resize 32x32 favicon-32.ico
convert your-logo.png -resize 48x48 favicon.ico
convert your-logo.png -resize 192x192 favicon.png
# Or use online tool: https://realfavicongenerator.net/
```
### Step 2: Create Custom CSS
Create `production/customizations/custom.css`:
```css
/* Brand Color Scheme */
:root {
--primary: #1a365d; /* Primary brand color (navbar, buttons) */
--secondary: #2c5282; /* Secondary brand color */
--accent: #3182ce; /* Accent color (links, hover states) */
--text-on-primary: #ffffff; /* Text color on primary background */
}
/* Apply primary color to navbar */
.navbar {
background-color: var(--primary) !important;
}
.navbar .navbar-brand,
.navbar .nav-link {
color: var(--text-on-primary) !important;
}
/* Primary buttons */
.btn-primary {
background-color: var(--primary) !important;
border-color: var(--primary) !important;
}
.btn-primary:hover {
background-color: var(--accent) !important;
border-color: var(--accent) !important;
}
/* Links */
a {
color: var(--accent) !important;
}
a:hover {
color: var(--primary) !important;
}
/* Hide "Powered by ERPNext" footer */
.footer-powered {
display: none !important;
}
/* Login page branding */
.login-content {
background-color: #f7fafc;
}
.login-content .card {
border: 1px solid var(--primary);
}
```
**Customize the colors:**
- Replace `#1a365d`, `#2c5282`, `#3182ce` with your brand colors
- Use a color picker to get hex codes from your logo
### Step 3: Configure Docker Volume Mounts
Add volume mounts to `production/production.yaml` to inject your customizations.
**Option A: Edit production.yaml directly** (regenerate will overwrite)
```bash
nano production/production.yaml
```
Find the `backend` service and add under `volumes:`:
```yaml
services:
backend:
volumes:
# ... existing volumes ...
- ./customizations/custom.css:/home/frappe/frappe-bench/sites/assets/custom.css:ro
- ./customizations/favicon.ico:/home/frappe/frappe-bench/sites/assets/favicon.ico:ro
- ./customizations/favicon.png:/home/frappe/frappe-bench/sites/assets/favicon.png:ro
- ./customizations/logo.png:/home/frappe/frappe-bench/sites/assets/logo.png:ro
```
Also add to `frontend` service:
```yaml
frontend:
volumes:
# ... existing volumes ...
- ./customizations/custom.css:/usr/share/nginx/html/assets/custom.css:ro
- ./customizations/favicon.ico:/usr/share/nginx/html/assets/favicon.ico:ro
- ./customizations/favicon.png:/usr/share/nginx/html/assets/favicon.png:ro
- ./customizations/logo.png:/usr/share/nginx/html/assets/logo.png:ro
```
**Option B: Create compose.custom.yaml overlay** (recommended, survives regeneration)
Create `production/compose.custom.yaml`:
```yaml
services:
backend:
volumes:
- ./customizations/custom.css:/home/frappe/frappe-bench/sites/assets/custom.css:ro
- ./customizations/favicon.ico:/home/frappe/frappe-bench/sites/assets/favicon.ico:ro
- ./customizations/favicon.png:/home/frappe/frappe-bench/sites/assets/favicon.png:ro
- ./customizations/logo.png:/home/frappe/frappe-bench/sites/assets/logo.png:ro
frontend:
volumes:
- ./customizations/custom.css:/usr/share/nginx/html/assets/custom.css:ro
- ./customizations/favicon.ico:/usr/share/nginx/html/assets/favicon.ico:ro
- ./customizations/favicon.png:/usr/share/nginx/html/assets/favicon.png:ro
- ./customizations/logo.png:/usr/share/nginx/html/assets/logo.png:ro
```
Then modify `scripts/deploy.sh` to include this overlay during generation (search for the `docker compose` command that generates production.yaml).
### Step 4: Apply Volume Mounts
```bash
# Restart services to mount new files
docker compose -f production/production.yaml restart backend frontend
# Verify files are mounted
docker compose -f production/production.yaml exec backend \
ls -la /home/frappe/frappe-bench/sites/assets/
# Should show: custom.css, favicon.ico, favicon.png, logo.png
```
### Step 5: Configure Site to Use Custom Assets
**Method 1: Via Website Settings (Recommended)**
1. Login to ERPNext: `https://erp.example.com`
2. Go to: **Setup → Website Settings**
3. Set the following:
- **Favicon**: Upload or set path to `/assets/favicon.ico`
- **Brand HTML**: Add custom logo if needed
- **Website Theme**: Create custom theme with your colors
- **Custom HTML**: Add CSS reference if needed
4. Go to: **Setup → System Settings**
- Upload app icon if needed
**Method 2: Via Bench Commands**
```bash
# Set site config to use custom CSS
docker compose -f production/production.yaml exec backend \
bench --site erp.example.com set-config app_include_css '["/assets/custom.css"]'
# Set favicon
docker compose -f production/production.yaml exec backend \
bench --site erp.example.com set-config app_logo_url '/assets/favicon.png'
# Clear cache and rebuild
docker compose -f production/production.yaml exec backend \
bench --site erp.example.com clear-cache
docker compose -f production/production.yaml exec backend \
bench --site erp.example.com build --force
```
**Method 3: Via site_config.json**
```bash
# Edit site config directly
docker compose -f production/production.yaml exec backend bash
# Inside container:
cd sites/erp.example.com
nano site_config.json
# Add these lines:
{
"app_include_css": ["/assets/custom.css"],
"app_logo_url": "/assets/favicon.png",
# ... other config ...
}
# Exit and rebuild
exit
docker compose -f production/production.yaml exec backend \
bench --site erp.example.com build --force
```
### Step 6: Verify Branding
```bash
# Clear cache
docker compose -f production/production.yaml exec backend \
bench --site erp.example.com clear-cache
# Rebuild assets
docker compose -f production/production.yaml exec backend \
bench --site erp.example.com build --force
# Restart services
docker compose -f production/production.yaml restart backend frontend
```
**In Browser:**
1. Open `https://erp.example.com`
2. Hard refresh: `Ctrl + Shift + R` (or `Cmd + Shift + R` on Mac)
3. Check browser tab for your favicon
4. Verify colors match your brand
5. Check that "Powered by ERPNext" is hidden
### Updating Branding
When you need to update your branding:
```bash
# 1. Edit customization files
nano production/customizations/custom.css
# or replace files:
cp ~/new-logo.png production/customizations/logo.png
# 2. Restart services (mounts are read-only, no rebuild needed)
docker compose -f production/production.yaml restart backend frontend
# 3. Clear cache and rebuild
docker compose -f production/production.yaml exec backend \
bench --site erp.example.com clear-cache
docker compose -f production/production.yaml exec backend \
bench --site erp.example.com build --force
# 4. Hard refresh browser (Ctrl+Shift+R)
```
### What Gets Customized
**Via File Mounts + CSS:**
- Favicon (browser tab icon)
- Brand colors (navbar, buttons, links)
- Login page styling
- Hide "Powered by ERPNext" footer
- Custom CSS overrides
**Via ERPNext Settings:**
- Website logo (Setup → Website Settings)
- App name (Setup → System Settings)
- Website theme colors
- Custom HTML/CSS includes
### Troubleshooting Branding
**Branding not showing:**
```bash
# 1. Check files exist
ls -la production/customizations/
# 2. Check files are mounted
docker compose -f production/production.yaml exec backend \
ls -la /home/frappe/frappe-bench/sites/assets/
# 3. Check site config
docker compose -f production/production.yaml exec backend \
bench --site erp.example.com show-config | grep -i css
# 4. Force rebuild
docker compose -f production/production.yaml exec backend \
bench --site erp.example.com clear-cache
docker compose -f production/production.yaml exec backend \
bench --site erp.example.com build --force
# 5. Restart services
docker compose -f production/production.yaml restart backend frontend
```
**CSS not applied:**
```bash
# Verify CSS is loaded (check browser console)
# Should see: /assets/custom.css
# If not, set in site_config.json:
docker compose -f production/production.yaml exec backend \
bench --site erp.example.com set-config app_include_css '["/assets/custom.css"]'
```
**Favicon not showing:**
```bash
# Set favicon URL
docker compose -f production/production.yaml exec backend \
bench --site erp.example.com set-config app_logo_url '/assets/favicon.png'
# Or via Website Settings in UI
```
### Advanced: Custom Frappe App for Branding
For complex branding needs, create a custom Frappe app:
```bash
# Inside backend container
docker compose -f production/production.yaml exec backend bash
# Create custom app
cd /home/frappe/frappe-bench
bench new-app custom_theme
# Add your customizations to:
# apps/custom_theme/custom_theme/public/css/
# apps/custom_theme/custom_theme/public/js/
# Install app to site
bench --site erp.example.com install-app custom_theme
# Exit container
exit
```
This approach is recommended for:
- Complex UI changes
- Multiple sites with different branding
- Custom JavaScript functionality
- Version-controlled branding
---
## Common Operations
### Backup Site
```bash
# Basic backup
./scripts/backup-site.sh erp.example.com
# Advanced backup with files and auto-copy
./scripts/backup-site.sh erp.example.com --with-files --auto-copy
# Encrypted backup with cleanup
BACKUP_PASSPHRASE='your-secret' ./scripts/backup-site.sh erp.example.com \
--encrypt --auto-copy --cleanup-old
```
**Backup Features:**
- **Database + Files**: Use `--with-files` to include uploaded files
- **Auto-copy**: `--auto-copy` copies backups to host `./backups/` directory
- **Encryption**: `--encrypt` with GPG AES256 (requires `BACKUP_PASSPHRASE`)
- **Cleanup**: `--cleanup-old` removes backups older than `BACKUP_RETENTION_DAYS`
- **Validation**: Automatic backup verification and size reporting
Backups are stored in: `sites/erp.example.com/private/backups/` (container) or `./backups/` (host)
### View Logs
```bash
# Interactive service selection
./scripts/logs.sh
# Specific services
./scripts/logs.sh backend # Application logs
./scripts/logs.sh frontend # Nginx logs
./scripts/logs.sh scheduler # Background jobs
./scripts/logs.sh all # All services
# Alternative: direct Docker commands
docker compose -f production/production.yaml logs -f backend
```
# Follow logs (live)
./production/scripts/logs.sh -f backend
```
### Stop Services
```bash
# Interactive mode (asks about dependencies)
./scripts/stop.sh
# Stop everything without prompts
./scripts/stop.sh --all
```
### Restart Services
```bash
./scripts/stop.sh --all
./scripts/deploy.sh
```
### Update ERPNext
```bash
# Update version in production.env
nano production/production.env
# Change: ERPNEXT_VERSION=v15.83.0
# Pull new images
docker compose -f production/production.yaml pull
# Redeploy
./production/scripts/deploy.sh
# Run migrations
docker compose -f production/production.yaml exec backend \
bench --site erp.example.com migrate
```
### Add New Site (Multi-tenancy)
```bash
# Update SITES in production.env
nano production/production.env
# Change: SITES=erp.example.com,erp2.example.com
# Regenerate configuration
./scripts/deploy.sh --regenerate
# Apply changes
docker compose -f production/production.yaml up -d
# Create new site
./scripts/create-site.sh erp2.example.com
# Apply branding to new site (manual steps)
# 1. Set CSS include
docker compose -f production/production.yaml exec backend \
bench --site erp2.example.com set-config app_include_css '["/assets/custom.css"]'
# 2. Set logo/favicon
docker compose -f production/production.yaml exec backend \
bench --site erp2.example.com set-config app_logo_url '/assets/favicon.png'
# 3. Build and clear cache
docker compose -f production/production.yaml exec backend \
bench --site erp2.example.com clear-cache
docker compose -f production/production.yaml exec backend \
bench --site erp2.example.com build --force
```
---
## Update Procedures
### Understanding the Update Layers
This deployment has **three layers** that update independently:
1. **Infrastructure Layer** (frappe_docker)
- Docker Compose configurations
- Container build instructions
- Deployment scripts structure
- **Updates via**: `git fetch upstream && git merge upstream/main`
2. **Application Layer** (ERPNext/Frappe)
- ERPNext features and bug fixes
- Frappe framework updates
- **Updates via**: Changing version tags in `production.env`
3. **Customization Layer** (Your Branding)
- Custom CSS, logos, favicon
- Your deployment scripts
- **Updates via**: Editing files in `production/customizations/`
### Update Infrastructure (Docker Configs)
**What this updates**: Compose files, build configs, deployment improvements
```bash
# 1. Sync with upstream frappe_docker
cd /path/to/erp-is-test
git fetch upstream
git merge upstream/main
# 2. Review infrastructure changes
git log --oneline upstream/main ^HEAD
git diff HEAD~1 compose.yaml
git diff HEAD~1 overrides/
# 3. Regenerate production.yaml with new infrastructure
./scripts/deploy.sh --regenerate
# 4. Review generated configuration
docker compose -f production/production.yaml config > /tmp/new-config.yaml
diff production/production.yaml /tmp/new-config.yaml || true
# 5. Apply infrastructure updates
docker compose -f production/production.yaml up -d
# 6. Verify all services healthy
docker compose -f production/production.yaml ps
```
**Important**: This does NOT update ERPNext version, only how it runs.
### Update ERPNext Version
**What this updates**: ERPNext features, bug fixes, Frappe framework
```bash
# 1. Check current version
docker compose -f production/production.yaml exec backend bench version
# 2. Backup before upgrading
./scripts/backup-site.sh erp.example.com --with-files --auto-copy
# 3. Update version in production.env
nano production/production.env
# Change:
# ERPNEXT_VERSION=v15.82.1 → ERPNEXT_VERSION=v15.85.0
# FRAPPE_VERSION=v15.82.1 → FRAPPE_VERSION=v15.85.0
# 4. Regenerate configuration
./scripts/deploy.sh --regenerate
# 5. Pull new images (downloads updated ERPNext)
docker compose -f production/production.yaml pull
# 6. Stop services
./scripts/stop.sh
# 7. Start with new version
docker compose -f production/production.yaml up -d
# 8. Run database migrations
docker compose -f production/production.yaml exec backend \
bench --site erp.example.com migrate
# 9. Clear cache and rebuild
docker compose -f production/production.yaml exec backend \
bench --site erp.example.com clear-cache
docker compose -f production/production.yaml exec backend \
bench --site erp.example.com build
# 10. Verify new version
docker compose -f production/production.yaml exec backend bench version
```
### Update Branding
**What this updates**: Your custom CSS, logos, favicon
```bash
# 1. Edit customization files
nano production/customizations/custom.css
# or replace:
cp ~/new-logo.png production/customizations/logo.png
# 2. Restart services to mount new files
docker compose -f production/production.yaml restart backend frontend
# 3. Clear cache and rebuild
docker compose -f production/production.yaml exec backend \
bench --site erp.example.com clear-cache
docker compose -f production/production.yaml exec backend \
bench --site erp.example.com build --force
# 4. Clear browser cache (Ctrl+Shift+R)
```
### Complete Update (All Layers)
**Use this for major version jumps or after long periods:**
```bash
# 1. Backup everything first
./scripts/backup-site.sh erp.example.com --with-files --auto-copy
# 2. Update infrastructure
git fetch upstream && git merge upstream/main
# 3. Update ERPNext version in production.env
nano production/production.env
# 4. Regenerate everything
./scripts/deploy.sh --regenerate
# 5. Deploy
./scripts/stop.sh
docker compose -f production/production.yaml pull
docker compose -f production/production.yaml up -d
# 6. Migrate
docker compose -f production/production.yaml exec backend \
bench --site erp.example.com migrate
# 7. Rebuild branding and cache
docker compose -f production/production.yaml exec backend \
bench --site erp.example.com clear-cache
docker compose -f production/production.yaml exec backend \
bench --site erp.example.com build --force
```
### Scheduled Maintenance Window
**Recommended schedule:**
```bash
# Monthly: Infrastructure updates
# - Review frappe_docker upstream changes
# - Merge if stability improvements available
# - Test in staging first
# Quarterly: ERPNext version updates
# - Check release notes: https://erpnext.com/version-15
# - Backup before upgrading
# - Update during low-traffic period
# As needed: Branding updates
# - No downtime required
# - Can be done anytime
```
### Rollback Procedures
**If update fails:**
```bash
# 1. Restore from backup
# (Backup is in sites/SITENAME/private/backups/ or ./backups/)
# 2. Revert version in production.env
nano production/production.env
# Change back to previous version
# 3. Regenerate and redeploy
./scripts/deploy.sh --regenerate
./scripts/stop.sh
docker compose -f production/production.yaml up -d
# 4. Restore database backup
docker compose -f production/production.yaml exec backend \
bench --site erp.example.com restore /path/to/backup/database.sql.gz
```
---
## Troubleshooting
### Site Not Accessible
**Check DNS:**
```bash
dig +short erp.example.com
nslookup erp.example.com
```
**Check Traefik:**
```bash
docker compose -f production/production.yaml logs traefik
```
**Check frontend:**
```bash
docker compose -f production/production.yaml logs frontend
```
### SSL Certificate Issues
**Check Let's Encrypt logs:**
```bash
docker compose -f production/production.yaml logs traefik | grep -i acme
```
**Common causes:**
- DNS not propagated (wait 24-48 hours)
- Ports 80/443 not open
- Rate limit (5 certs/week per domain)
**Test certificate:**
```bash
curl -vI https://erp.example.com 2>&1 | grep -i certificate
```
### Database Connection Failed
**Check MariaDB:**
```bash
docker compose -f production/production.yaml logs mariadb
```
**Test connection:**
```bash
docker compose -f production/production.yaml exec backend \
mysql -h mariadb-database -u root -p
# Enter DB_PASSWORD from production.env
```
**Common causes:**
- `DB_PASSWORD` mismatch between `production.env` and `mariadb.env`
- `DB_HOST` should be `mariadb-database` (container name)
### Branding Not Applied
**Check files exist:**
```bash
ls -la production/customizations/
# Should show: custom.css, favicon.ico, favicon.png, logo.png
```
**Check mounts:**
```bash
docker compose -f production/production.yaml exec backend \
ls -la /home/frappe/frappe-bench/sites/assets/
```
**Reapply branding manually:**
```bash
# 1. Verify volume mounts in production.yaml
grep -A5 "customizations" production/production.yaml
# 2. Restart services
docker compose -f production/production.yaml restart backend frontend
# 3. Set CSS and favicon in site config
docker compose -f production/production.yaml exec backend \
bench --site erp.example.com set-config app_include_css '["/assets/custom.css"]'
docker compose -f production/production.yaml exec backend \
bench --site erp.example.com set-config app_logo_url '/assets/favicon.png'
# 4. Clear cache and rebuild
docker compose -f production/production.yaml exec backend \
bench --site erp.example.com clear-cache
docker compose -f production/production.yaml exec backend \
bench --site erp.example.com build --force
```
### Service Crash Loops
**Check logs:**
```bash
./scripts/logs.sh <service-name>
```
**Check resources:**
```bash
docker stats
free -h
df -h
```
**Restart:**
```bash
./scripts/stop.sh --all
./scripts/deploy.sh
```
### Performance Issues
**Check worker count:**
```bash
docker ps | grep erpnext-production
```
**Scale workers** (edit `production/production.env`):
```env
WORKERS=4
QUEUE_WORKERS=6
```
Then redeploy:
```bash
./production/scripts/deploy.sh --regenerate # Regenerate config
./production/scripts/deploy.sh # Apply changes
```
---
## Security
### 1. Never Commit Secrets
```bash
# Verify .env files are ignored
git check-ignore production/production.env
git check-ignore production/mariadb.env
git check-ignore production/traefik.env
# All should return the filename (means they're ignored)
```
### 2. Use Strong Passwords
```bash
# Always generate with OpenSSL
openssl rand -base64 32 # 32 characters
openssl rand -base64 48 # 48 characters (more secure)
```
### 3. Restrict File Permissions
```bash
chmod 600 production/*.env
chmod 755 production/scripts/*.sh
```
### 4. Enable Firewall
```bash
sudo ufw enable
sudo ufw status
```
### 5. Keep System Updated
```bash
# System updates
sudo apt update && sudo apt upgrade -y
# Docker updates
sudo apt install --only-upgrade docker-ce docker-compose-plugin
```
### Setup Automated Backups
```bash
# Add to crontab
crontab -e
# Daily backup at 2 AM with auto-copy and cleanup
0 2 * * * cd /path/to/erp-is-test/production && AUTO_COPY=1 CLEANUP_OLD=1 ./scripts/backup-site.sh erp.example.com
# Weekly encrypted backup
0 3 * * 0 cd /path/to/erp-is-test/production && BACKUP_PASSPHRASE='your-secret' ./scripts/backup-site.sh erp.example.com --encrypt --auto-copy
```
### Backup Encryption
**Setup GPG for backups:**
```bash
# Install GPG (usually pre-installed)
sudo apt install gnupg -y
# Set backup passphrase
export BACKUP_PASSPHRASE='your-very-secure-passphrase'
# Create encrypted backup
./scripts/backup-site.sh erp.example.com --encrypt --auto-copy
```
**Decrypt backups:**
```bash
# Decrypt single file
gpg --decrypt backup-file.gpg > backup-file
# Decrypt with passphrase from environment
echo "$BACKUP_PASSPHRASE" | gpg --batch --passphrase-fd 0 --decrypt file.gpg > file
# Decrypt all .gpg files in directory
for f in *.gpg; do gpg --decrypt "$f" > "${f%.gpg}"; done
```
### Backup Monitoring
**Check backup status:**
```bash
# View backup logs
tail -f /tmp/erpnext-backup-$(date +%Y%m%d).log
# List backups
ls -lah ./backups/
# Verify backup integrity
./scripts/backup-site.sh erp.example.com --debug
```
### 7. Monitor Logs
```bash
# Setup log rotation
sudo nano /etc/logrotate.d/docker-compose
# Add:
/path/to/erp-is/production/logs/*.log {
daily
rotate 14
compress
delaycompress
missingok
notifempty
}
```
### 8. Backup Your Passwords
**Store securely in password manager:**
- Server IP and SSH credentials
- Domain and DNS credentials
- Database passwords (DB_PASSWORD)
- Admin password (ADMIN_PASSWORD)
- Traefik dashboard password
- Backup encryption passphrase
- SSL certificate information
### Environment Variables Reference
**Global Script Variables:**
```bash
# Project configuration
PROJECT_NAME=erpnext-production # Docker project name
BACKUP_RETENTION_DAYS=30 # Backup retention period
# Backup script variables
AUTO_COPY=1 # Auto-copy to host
CLEANUP_OLD=1 # Auto-cleanup old backups
BACKUP_PASSPHRASE='your-secret' # GPG encryption passphrase
DEBUG=1 # Enable debug output
# Usage example
AUTO_COPY=1 CLEANUP_OLD=1 DEBUG=1 ./scripts/backup-site.sh erp.example.com
```
**Backup Environment Variables:**
```bash
# Set persistent environment variables
echo 'export BACKUP_PASSPHRASE="your-secure-passphrase"' >> ~/.bashrc
echo 'export AUTO_COPY=1' >> ~/.bashrc
echo 'export CLEANUP_OLD=1' >> ~/.bashrc
source ~/.bashrc
# Now backups are simpler
./scripts/backup-site.sh erp.example.com --encrypt
```
---
## Environment Files Explained
### Why Three Files?
Different Docker Compose projects use different files:
1. **Traefik project**`traefik.env`
- Reverse proxy configuration
- SSL certificate management
- Dashboard access
2. **MariaDB project**`mariadb.env`
- Database root password
- Shared across all ERPNext instances
3. **ERPNext project**`production.env`
- Application configuration
- Database connection
- Redis configuration
- Admin password
### Critical: DB Password Must Match
```env
# production.env
DB_PASSWORD=Q2f7k9Lm3nP5rT8wX1zC4vB6hJ0yN2sA
# mariadb.env
DB_PASSWORD=Q2f7k9Lm3nP5rT8wX1zC4vB6hJ0yN2sA # ← MUST BE SAME!
```
If different, ERPNext cannot connect to database.
---
## Architecture Explained
### Image Sources
This setup uses **official Frappe Docker images**:
```yaml
# Your fork provides:
- Infrastructure: compose.yaml, deployment scripts
- Customizations: production/customizations/, branding
# Frappe provides:
- Container images: frappe/erpnext, frappe/frappe
- Base configurations: upstream compose files
```
**Image pull locations:**
```bash
# From Docker Hub (official Frappe images)
frappe/erpnext:v15.82.1
frappe/frappe:v15.82.1
library/mariadb:11.8
library/redis:alpine
traefik:v2.11
```
**You maintain:**
- `production/` directory (deployment scripts, configs)
- `production/customizations/` (branding files)
- `.gitignore` (excludes *.env files)
**You track upstream:**
- `compose.yaml` (base infrastructure)
- `overrides/compose.*.yaml` (feature overlays)
- `images/*/Containerfile` (if you need custom builds)
### Why This Approach?
**Benefits:**
- ✅ Get official, tested ERPNext images
- ✅ Receive infrastructure updates from frappe_docker
- ✅ Keep your customizations separate
- ✅ Easy to merge upstream improvements
- ✅ No need to rebuild images for simple branding
**When you'd build custom images:**
- Need to modify Python dependencies
- Add system packages to containers
- Install custom Frappe apps (beyond branding)
---
## Script Optimizations
All automation scripts have been significantly optimized for better performance, maintainability, and user experience.
### 📊 Optimization Results
| Script | Before | After | Reduction | Features Added |
|--------|--------|-------|-----------|----------------|
| **backup-site.sh** | 469 lines | 224 lines | **52%** | Help, encryption, validation |
| **create-site.sh** | 118 lines | 96 lines | **19%** | Help, better prompts |
| **deploy.sh** | 287 lines | 156 lines | **46%** | Help, cleaner output |
| **logs.sh** | 106 lines | 65 lines | **39%** | Help, menu interface |
| **stop.sh** | 85 lines | 68 lines | **20%** | Help, --all option |
| **validate-env.sh** | 238 lines | 241 lines | **+1%** | Help (already optimal) |
| **TOTAL** | **1,303** | **850** | **35%** | **100% help coverage** |
### 🚀 Key Improvements
**1. Comprehensive Help System**
```bash
# Every script now has detailed help
./scripts/deploy.sh --help
./scripts/backup-site.sh -h
./scripts/logs.sh --help
```
**2. Condensed Code Structure**
- One-liner helper functions
- Compact conditional statements
- DRY (Don't Repeat Yourself) principles
- Smart use of bash shortcuts
**3. Enhanced Backup Features**
- **GPG Encryption**: AES256 symmetric encryption
- **Auto-copy**: Container → host backup transfer
- **Cleanup**: Automated old backup removal
- **Validation**: File verification and size reporting
- **Logging**: Detailed operation logs
**4. Interactive Log Viewer**
- Menu-driven service selection
- Support for both numbers (1-7) and names
- Real-time log following
- Clear service descriptions
**5. Smart Deployment**
- Built-in validation before deployment
- Cleaner progress output
- Helper functions for repetitive tasks
- Better error handling
**6. Improved Stop Script**
- Selective service stopping
- `--all` flag for automation
- Interactive confirmation for dependencies
### 📝 Technical Details
**Before (verbose):**
```bash
echo_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
if [ -z "$SITE_NAME" ]; then
echo_error "Site name cannot be empty"
exit 1
fi
```
**After (lean):**
```bash
echo_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
[[ -z "$SITE_NAME" ]] && { echo_error "Site name cannot be empty"; exit 1; }
```
**Result:** 60% fewer lines with identical functionality.
### 🛡️ Maintained Compatibility
**All original functionality preserved**
**Same command-line interfaces**
**No breaking changes**
**Enhanced error handling**
**Better user experience**
For detailed optimization information, see: `scripts/OPTIMIZATION_SUMMARY.md`
---
## Support
- **ERPNext Forum**: https://discuss.erpnext.com/
- **Documentation**: https://docs.erpnext.com/
- **Upstream Repository**: https://github.com/frappe/frappe_docker
- **GitHub Issues**: https://github.com/frappe/frappe_docker/issues
---
**Tested With**: ERPNext v15.82.1, Docker 24.0+, Ubuntu 22.04 LTS
**Script Optimization**: 35% reduction in code, 100% help coverage
**Last Updated**: October 2025