- Add CLAUDE.md with repository guidance for Claude Code - Add apps.json defining ERPNext and HRMS apps - Add build-custom-image.sh script for building custom images - Add docs/README-CUSTOM-SETUP.md with setup instructions - Update pwd.yml to use custom image and install HRMS app This enables a complete custom deployment with ERPNext (full ERP) and HRMS (HR management) using Docker containers.
10 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Overview
This repository provides Docker containerization for Frappe Framework and ERPNext. It supports both development environments and production deployments through multiple compose configurations and image types.
Repository Architecture
Multi-Image Strategy
There are four distinct Dockerfile types in images/:
- bench (
images/bench/Dockerfile): CLI-only setup for development/debugging - custom (
images/custom/Containerfile): Production-ready, built from Python base, usesapps.jsonfor app installation - layered (
images/layered/Containerfile): Same as custom but based on prebuilt Docker Hub images for faster builds - production (
images/production/Containerfile): Quick-start image with only Frappe + ERPNext (not customizable)
Key principle: custom and layered are for real deployments; production is for exploration only.
Multi-Service Architecture
The base compose.yaml defines these core services:
- configurator: Runs once on startup to configure
common_site_config.jsonwith database and Redis connection details, then exits - backend: Werkzeug development server (Python) serving dynamic content
- frontend: Nginx reverse proxy serving static assets and routing requests
- websocket: Node.js Socket.IO server for real-time features
- queue-short/queue-long: Python RQ workers for background job processing
- scheduler: Python service running scheduled tasks
Additional services come from compose overrides in overrides/:
compose.mariadb.yaml/compose.postgres.yaml: Database servicescompose.redis.yaml: Redis cache and queue servicescompose.proxy.yaml: Traefik reverse proxy for multi-site hostingcompose.https.yaml: SSL/TLS certificate managementcompose.noproxy.yaml: Direct port exposure (no proxy) for development
Compose File Composition Pattern
The architecture uses Docker Compose overrides to build complete environments:
docker compose \
-f compose.yaml \
-f overrides/compose.mariadb.yaml \
-f overrides/compose.redis.yaml \
-f overrides/compose.noproxy.yaml \
config > compose.custom.yaml
This pattern allows mixing and matching services based on deployment needs.
Common Development Commands
Quick Testing Setup (pwd.yml)
For rapid evaluation without local setup:
# Start all services (uses frappe-custom:v15 image)
docker compose -f pwd.yml up -d
# Monitor site creation (takes ~5 minutes)
docker compose -f pwd.yml logs -f create-site
# Access at http://localhost:8080
# Login: Administrator / admin
The pwd.yml file is a self-contained configuration that includes all services (MariaDB, Redis, workers, etc.) and automatically creates a site named "frontend".
Development Environment Setup
Full development with hot-reload:
# 1. Copy devcontainer configuration
cp -R devcontainer-example .devcontainer
# 2. Open in VSCode with Dev Containers extension
# VSCode will detect .devcontainer and prompt to reopen in container
# 3. Inside container, run automated installer
cd /workspace/development
python installer.py
# Installer arguments:
# -j, --apps-json: Path to apps.json (default: apps-example.json)
# -b, --bench-name: Bench directory name (default: frappe-bench)
# -s, --site-name: Site name, must end with .localhost (default: development.localhost)
# -t, --frappe-branch: Frappe branch (default: version-15)
# -d, --db-type: Database type (mariadb or postgres)
# -a, --admin-password: Admin password (default: admin)
Development files are located in development/frappe-bench/ (git-ignored directory).
Bench Commands (Inside Container)
# Site management
bench new-site --mariadb-user-host-login-scope=% <site-name>
bench list-sites
bench --site <site-name> migrate
# App management
bench get-app --branch <branch> <git-url>
bench --site <site-name> install-app <app-name>
bench list-apps
# Development
bench start # Start dev server with hot-reload
bench build # Build frontend assets
bench build --app <app-name> # Build specific app
# Database
bench mariadb # Open MariaDB console
bench --site <site-name> backup --with-files
# Debugging
bench --site <site-name> console # Python REPL with Frappe context
Building Custom Images
Creating apps.json
Define custom apps to install in apps.json:
[
{
"url": "https://github.com/frappe/erpnext",
"branch": "version-15"
},
{
"url": "https://github.com/frappe/hrms",
"branch": "version-15"
}
]
Build Process
# 1. Encode apps.json
export APPS_JSON_BASE64=$(base64 -w 0 apps.json)
# 2. Build image (example using layered)
docker build \
--build-arg=FRAPPE_PATH=https://github.com/frappe/frappe \
--build-arg=FRAPPE_BRANCH=version-15 \
--build-arg=APPS_JSON_BASE64=$APPS_JSON_BASE64 \
--tag=custom:15 \
--file=images/layered/Containerfile .
# Or use docker-bake.hcl for multi-arch builds
docker buildx bake --no-cache
Important build args:
FRAPPE_PATH: Frappe framework repo URLFRAPPE_BRANCH: Frappe framework branchAPPS_JSON_BASE64: Base64-encoded apps.jsonPYTHON_VERSION: Python version (default: 3.11.6)NODE_VERSION: Node.js version (default: 20.19.2)
Testing and CI
Linting
# Install pre-commit
pip install pre-commit
# or
brew install pre-commit
# Setup hooks
pre-commit install
# Run on all files
pre-commit run --all-files
Integration Tests
# Setup test environment
python3 -m venv venv
source venv/bin/activate
pip install -r requirements-test.txt
# Run tests
pytest
Site Operations
Creating a New Site
# Basic site creation
docker compose exec backend \
bench new-site --mariadb-user-host-login-scope=% \
--db-root-password <db-password> \
--admin-password <admin-password> <site-name>
# For PostgreSQL
docker compose exec backend bench set-config -g root_login postgres
docker compose exec backend bench set-config -g root_password <password>
docker compose exec backend \
bench new-site --db-type postgres \
--admin-password <admin-password> <site-name>
Note: The --mariadb-user-host-login-scope=% option is critical for Docker networking - it allows database users to connect from any host (%).
Accessing Container Files
# Enter backend container
docker compose exec backend bash
# Key directories inside container:
# /home/frappe/frappe-bench/apps/ - All Frappe applications
# /home/frappe/frappe-bench/sites/ - Site data and configuration
# /home/frappe/frappe-bench/logs/ - Application logs
# Copy files from container to host
docker compose cp backend:/home/frappe/frappe-bench/apps/my_app ./local-apps/
Environment Variables
The main environment variables (from example.env):
ERPNEXT_VERSION: Version tag for ERPNext imageDB_HOST,DB_PORT: External database connection (if not using compose.mariadb.yaml)REDIS_CACHE,REDIS_QUEUE: External Redis connection (if not using compose.redis.yaml)FRAPPE_SITE_NAME_HEADER: Override site resolution (default:$$host)HTTP_PUBLISH_PORT: HTTP port to publish (default: 8080)LETSENCRYPT_EMAIL: Email for Let's Encrypt certificatesSITES: Comma-separated list of sites for SSL certificates
See docs/container-setup/env-variables.md for complete reference.
Key Implementation Details
Service Startup Dependencies
The configurator service MUST complete successfully before other services start. It writes database and Redis connection details to sites/common_site_config.json. The compose file uses:
x-depends-on-configurator: &depends_on_configurator
depends_on:
configurator:
condition: service_completed_successfully
Nginx Configuration
The frontend service uses nginx-entrypoint.sh which dynamically generates Nginx configuration from resources/nginx-template.conf using environment variables like BACKEND, SOCKETIO, FRAPPE_SITE_NAME_HEADER.
Volume Mounts
- sites: Shared across all services, contains site data and configuration
- logs: Application logs (optional, for debugging)
For development, bind mount ./development/frappe-bench to /workspace/development in the container.
Platform-Specific Notes
ARM64 / Apple Silicon
# Build multi-arch images
docker buildx bake --no-cache --set "*.platform=linux/arm64"
# For pwd.yml on ARM64
# 1. Add platform: linux/arm64 to all services
# 2. Replace version tags with :latest
Use :cached or :delegated volume flags on macOS for better performance.
Project Structure Reference
frappe_docker/
├── compose.yaml # Base compose file (core services)
├── pwd.yml # Self-contained quick-start config
├── docker-bake.hcl # Buildx bake configuration
├── example.env # Environment variables template
├── images/ # Dockerfile definitions
│ ├── bench/ # CLI-only image
│ ├── custom/ # Production image (apps.json)
│ ├── layered/ # Fast-build production image
│ └── production/ # Quick-start image (Frappe + ERPNext only)
├── overrides/ # Compose file extensions
│ ├── compose.mariadb.yaml
│ ├── compose.redis.yaml
│ ├── compose.proxy.yaml
│ └── compose.https.yaml
├── resources/ # Runtime templates
│ ├── nginx-entrypoint.sh
│ └── nginx-template.conf
├── development/ # Dev environment (git-ignored)
│ └── installer.py # Automated setup script
├── devcontainer-example/ # VSCode devcontainer template
└── docs/ # Documentation
Version Compatibility
- Frappe v14: Python 3.10.13, Node v16
- Frappe v15: Python 3.11.6, Node v20 (default)
- Frappe v13: Python 3.9.17, Node v14
When working with different versions, set PYENV_VERSION and use nvm to switch Node versions inside the container.
Important Development Notes
- Site names must end with
.localhostfor local development - Default MariaDB root password is
123in development - Default admin password is
adminwhen using installer.py - Wait for configurator to exit before creating sites or running migrations
- The
development/directory is git-ignored - all local development happens here - Use
--mariadb-user-host-login-scope=%when creating sites in Docker to enable proper networking