: / Knowledge Base / ERPNext Docker

ERPNext Docker


Created:3/30/2026


There is an official Frappe-maintained Docker setup, but it is quite complicated. That complexity is understandable - it tries to support multiple use cases. If you truly understand the setup, there's no doubt it's reliable and production-ready.

For my case, I want a simple, straightforward setup that is still performant and reliable.

Understanding Frappe Images

Excalidraw diagram

Frappe publishes three pre-built images to Docker Hub, each serving a different purpose:

  • frappe/base - Image with run dependencies only
  • frappe/build - Image with build dependencies on top of base
  • frappe/erpnext - Image with run dependencies + Frappe & ERPNext apps installed
ImageTagged byRun Deps.Build Deps.Apps Frappe & ERPNext
frappe/baseFRAPPE_VERSION
frappe/buildERPNEXT_VERSION
frappe/erpnextERPNEXT_VERSION

For a production image, you need two things: run dependencies and installed apps. Build dependencies are only needed during the build process and should not be included in the final image - they add unnecessary size.

There are multiple ways to build a production-ready image:

  1. Frappe Official Production Images - This produces the frappe/erpnext image that Frappe builds and pushes to Docker Hub. The frappe/base and frappe/build images are also built from this file but are helper images used during the build process.
  2. Frappe Official Layered Images - This image is built by using frappe/build as a builder stage and copying the app artifacts into a clean frappe/base image.
  3. Frappe Official Custom Images - This is almost identical to the production image. It could be used when a high level of customization is needed.

Essentially, the build process comes down to these steps:

  1. Start from python:slim, install run dependencies, save as base
  2. From base, install build dependencies, save as build
  3. From build, initialize bench and install apps - the bench folder is the build artifact that will be copied into the base image
  4. From base, copy the bench folder artifact into the image - this is your final production image

This multi-stage approach keeps the final image lean by discarding the build tools after the apps are compiled and installed.

Technical Details

The Dockerfile for all three images lives in images/production.

It is triggered by docker-build-push.yml. The workflow uses docker/bake-action, which reads the docker-bake.hcl file to determine what to build.

In that docker-bake.hcl you'll find the Dockerfile targets and image tags for each image variant.

Our Image

Why?

Frappe already publishes official images, so why build another one?

Faster custom image build time

When you need to add a custom app on top of ERPNext, the official approach requires you to re-initialize bench and reinstall Frappe & ERPNext from scratch on every build. This is slow - bench initialization and app installation can take several minutes each time.

Our image pre-bakes bench initialization and the Frappe & ERPNext installation into a reusable base. Your custom image build only needs to install your custom app on top, which will be faster.

Clear starting point with explicit versions

With specific version tags, you know exactly which version of Frappe and ERPNext you're building on. There's no ambiguity about what's inside the image.

Cons

Larger image size

Our image is larger than the official image - around 1 GB compared to the official ~500 MB. This is because we include build dependencies alongside the run dependencies and installed apps.

However, we don't use this image directly in production. It's used only as a builder stage in a multi-stage Dockerfile. The final production image is built by copying the bench folder from our image into a clean frappe/base, so the final size ends up comparable to the official image.

ImageTagged byRun Deps.Build Deps.Apps Frappe & ERPNext
thspacecode/erpnext-dockerFRAPPE_ERPNEXT_VERSION

The Image

Excalidraw diagram

Based on the official image, we initialize bench and install Frappe & ERPNext, run a simple test to verify the image can start the web service, and then push the result to Docker Hub. This gives us a reliable, version-pinned base that custom app builds can start from - without repeating the slow setup every time.


Need a hand?We're here to help you solve it - fast, simple, and stress-free.
Hire Us