Custom ERPNext Docker images and ready-to-run deployment recipes, maintained by SpaceCode.
The images pre-bake bench init and the Frappe/ERPNext install on top of frappe/build, so they can be reused as a builder stage for your own custom-app images — without repeating the slow bench bootstrap on every build.
📖 Background and design rationale: Understanding ERPNext Docker images
Images are published to Docker Hub: thspacecode/erpnext-docker
Frappe ships official images (frappe/base, frappe/build, frappe/erpnext), and the standard way to build a custom app is a multi-stage Dockerfile that, on every build, runs bench init and re-installs Frappe + ERPNext before adding your app. That bootstrap is the slow part.
This project moves that work into a reusable, version-tagged base image:
- Faster custom-app builds —
bench init+ Frappe/ERPNext are already baked in. Your build only adds your app and compiles assets. - Version clarity — a pinned tag tells you exactly which Frappe and ERPNext versions you are building on.
Trade-off: because it keeps the build dependencies, the base image is ~1 GB (vs. the official ~500 MB runtime image). Since it is meant to be consumed as a builder stage in a multi-stage build, your final production image stays just as small.
A single multi-stage dockerfile/Dockerfile produces three chained images, each published under its own tag:
| Target | Docker Hub tag | What's inside | Use it for |
|---|---|---|---|
base |
version-16-latest |
frappe/build + bench init + ERPNext, pre-baked |
Builder stage for custom apps; the dev & production Compose setups |
all-in-one |
ALL-version-16-latest |
base + MariaDB, Redis, nginx, supervisor — site is created on first boot |
Single-container trials, Railway (sleepable), Dokploy previews |
pre-install |
PRE-version-16-latest |
all-in-one, but the site + ERPNext are created at build time |
Quick trials, demos & agentic / ephemeral workflows (instant boot) |
Each variant also gets an immutable, version-pinned tag derived from the actual installed versions, e.g. 16-F<frappe>.<x>_E<erpnext>.<y> (and the ALL- / PRE- prefixed equivalents).
Tip — agentic & ephemeral workflows: the
pre-installimage already contains a created site with ERPNext installed, so a container boots into a ready instance in seconds — no site creation or app install on first run. That makes it a great fit for agentic or CI pipelines that spin up a throwaway ERPNext on demand: the agent only has tobench get-appits custom app on top and start working.
The fastest way to see ERPNext running — no clone, no build, no Compose. The pre-install image already has a site created and ERPNext installed, so it boots straight into a ready instance:
docker run -p 8080:80 thspacecode/erpnext-docker:PRE-version-16-latestGive it a few seconds to boot, then open http://localhost:8080 and log in as Administrator / admin.
The image bundles its own MariaDB and Redis, so the trial is fully self-contained. It's a throwaway —
docker rmthe container to wipe it and start fresh.
Each folder is self-contained and has its own step-by-step README. For a quick throwaway trial, see Quick start above.
| Setup | Folder | Best for |
|---|---|---|
| Development | setup-dev-docker-compose |
Building custom Frappe apps in a VS Code Dev Container |
| Production | setup-prod-docker-compose |
Self-hosting with the services split into containers |
| Railway | setup-railway-separated |
One-click cloud hosting on Railway |
- Development and Production run the
baseimage alongside dedicated MariaDB and Redis containers. - Production splits the stack into web (gunicorn), websocket, short/long workers, scheduler, nginx, MariaDB and Redis, and serves on port 80 — put a reverse proxy (Traefik, Caddy, …) in front for TLS.
- Railway layers supervisor + nginx onto the
baseimage and runs the Frappe services together, because Railway currently allows only one service per volume.
Build any target from the dockerfile/ directory with --target:
cd dockerfile
# Base (builder) image
docker buildx build --target base -t erpnext-docker:version-16-latest .
# All-in-one (DB + Redis + nginx + supervisor, site created on first boot)
docker buildx build --target all-in-one -t erpnext-docker:ALL-version-16-latest .
# Pre-install (site + ERPNext baked at build time)
docker buildx build --target pre-install -t erpnext-docker:PRE-version-16-latest .The Frappe/ERPNext branch and repos are configurable via build args (FRAPPE_BRANCH, FRAPPE_REPO, ERPNEXT_BRANCH, ERPNEXT_REPO). The pre-install target additionally accepts SITE_NAME, ADMIN_PASSWORD and DB_ROOT_PASSWORD.
# Reuse the pre-baked bench + ERPNext
FROM thspacecode/erpnext-docker:version-16-latest AS builder
WORKDIR /home/frappe/frappe-bench
RUN bench get-app --resolve-deps https://github.com/your-org/your_app
# ...then copy the built bench into a slim runtime image (e.g. frappe/base).github/workflows/push-docker.yml runs on every push to main and weekly (Sunday 00:00). It:
- Builds the
baseandpre-installtargets. - Smoke-tests the
pre-installimage — boots a container and asserts an HTTP200from the ready site. - Reads the installed versions via
bench versionand computes the version tag. - Builds and pushes
base,all-in-oneandpre-installto Docker Hub with bothlatestand version-pinned tags.
.
├── dockerfile/ # Source for the published images
│ ├── Dockerfile # single multi-stage build (base → all-in-one → pre-install)
│ ├── all-in-one/conf/ # setup.sh + nginx.conf + supervisord.conf
│ └── pre-install/conf/ # init.sh (build-time setup) + start.sh (runtime entrypoint)
├── setup-dev-docker-compose/ # VS Code Dev Container for app development
├── setup-prod-docker-compose/ # Multi-container production stack
├── setup-railway-separated/ # Deploy to Railway
└── .github/workflows/ # Build, smoke-test, tag & push to Docker Hub
- Understanding ERPNext Docker images — the article this repo accompanies
- Frappe / ERPNext system architecture
- Official
frappe_docker - ERPNext · Frappe Framework