Frappe Devstack
Batteries-included Docker Compose framework for Frappe v16: one CLI from zero to a running bench, plus a hardened production stack (Traefik + Let's Encrypt TLS, multi-tenancy, backups) and local staging.
- Author: Venkateshvenki404224
- Repository: https://github.com/Venkateshvenki404224/frappe-devstack
- GitHub stars: 1
- Forks: 0
- License: MIT
- Category: Developer Tools
- Maintenance: Actively Maintained
Install Frappe Devstack
bench get-app https://github.com/Venkateshvenki404224/frappe-devstack
Tags
- devops
- docker
- docker-compose
- erpnext
- frappe
- frappe-framework
- self-hosted
- traefik
Add the Frappe Gems badge to your README
Maintain Frappe Devstack? Paste this into your README:
[](https://frappegems.com/gems/apps/Venkateshvenki404224/frappe-devstack)
About Frappe Devstack
frappe-devstack
A batteries-included Docker Compose framework for Frappe v16 — one CLI takes you from zero to a running bench locally, and the same repo ships a hardened production stack (Traefik + automatic TLS, multi-tenancy, backups) you can deploy to a real host.
You declare the apps you want in a small JSON preset; frappe-devstack builds the image, generates config, brings up the full runtime (backend, frontend, websocket, scheduler, queue workers, MariaDB, Redis), and creates a site with those apps installed.
Why
frappe_docker is powerful but low-level. frappe-devstack wraps the same building blocks in an opinionated CLI so the common path is one command:
- One preset → one command. List apps + branches in
presets/.json;python entry.py setupdoes the rest. - Dev that's actually editable. App sources are cloned to
./apps/and bind-mounted, so editing Python is live (docker compose restart backend) — no rebuild. - Private apps, safely. A
${GITHUB_PAT}placeholder in a preset URL is expanded from.env.secrets, stripped from the image build-arg, and supplied to BuildKit as a--mount=type=secret— so the token never lands in image layers ordocker history. - Dev → staging → prod, same artifact. The production stack runs the immutable baked image; staging runs that exact image locally so you rehearse before the live host.
Requirements
- Docker Engine 23+ with the Compose v2 plugin (BuildKit is on by default).
- Python 3.8+ (standard library only — no
pip installneeded). git.
Quick start (local dev)
git clone https://github.com/Venkateshvenki404224/frappe-devstack.git
cd frappe-devstack
# First run: build image, generate .env, start the stack, create a site.
python entry.py setup --preset example
# Day-to-day
python entry.py restart # restart services
python entry.py stop # stop services
python entry.py destroy # tear down containers + volumes (prompts 'yes')
# Rebuild the image after editing a preset
python entry.py build --preset example
When setup finishes it prints the URL (default http://localhost:8080, auto-bumped if the port is taken), the Administrator login, and the generated password. Admin and DB passwords are generated on first run and written to .env — never commit .env.
> python entry.py with no arguments is the same as setup (the first-run wizard).
Presets
A preset is a JSON array of { "url", "branch" } objects — the apps baked on top of Frappe. The bundled presets/example.json:
| App | Branch |
|---|---|
| frappe | version-16 (set by FRAPPE_VERSION, cloned automatically) |
| erpnext | version-16 |
| hrms | version-16 |
Add more by editing the file (apps track their own branch lines — e.g. wiki uses version-3):
[
{ "url": "https://github.com/frappe/erpnext", "branch": "version-16" },
{ "url": "https://github.com/frappe/hrms", "branch": "version-16" },
{ "url": "https://github.com/frappe/wiki", "branch": "version-3" }
]
After changing a preset, run python entry.py build --preset. The existing site won't auto-install newly added apps — install them explicitly:
docker compose exec backend bench --site frontend install-app
docker compose exec backend bench --site frontend migrate
Create as many presets as you like (presets/.json) and select one with --preset.
Adding a private app (GitHub PAT)
Reference a private repo with a ${GITHUB_PAT} placeholder — see presets/private-app.example.json:
{ "url": "https://${GITHUB_PAT}@github.com/your-org/your-private-app.git", "branch": "main" }
Provide the token (a fine-grained, read-only token scoped to just that repo, Contents: read):
cp .env.secrets.example .env.secrets
# edit .env.secrets: GITHUB_PAT=github_pat_xxx
entry.py reads .env.secrets at runtime (never writes to it; it's gitignored), expands the placeholder for the host clone into ./apps/, and for the image build strips the token from apps.json and passes it to BuildKit as a secret. If GITHUB_PAT is empty, public clones still work and a clear warning is printed for private ones.
How it works
Build-time vs. run-time apps
Two app installations stay in sync:
- Baked into the image — the multi-stage
Dockerfilerunsbench initagainst anapps.jsonpassed as the credential-freeAPPS_JSON_BASE64build-arg; the PAT (if any) arrives as a BuildKit secret..gitdirs are stripped from the result. - Bind-mounted at runtime —
docker-compose.ymlmounts./appsover/home/frappe/frappe-bench/apps, and the CLI clones the same apps there so containers see editable source.
Editing ./apps// is live for Python (docker compose restart backend). But bench init-time wiring and built assets come from the baked copy — so adding or removing an app requires a rebuild (python entry.py build).
Runtime topology
Boot order is gated by depends_on + healthchecks:
db(MariaDB 10.6, healthchecked)configurator(one-shot) — writescommon_site_config.json(db/redis endpoints) andapps.txt.create-site(one-shot) — waits for db + redis, thenbench new-site frontend --install-app.backend(gunicorn),frontend(nginx → hostPORT),websocket,scheduler,queue-long,queue-short.redis-cache,redis-queue.
State lives in named volumes (db-data, redis-*-data, sites, assets, logs) that survive stop/restart and are wiped by destroy. ./apps/ is a host bind mount (gitignored) and persists across destroys — delete it manually for fresh clones.
Production & staging
This repo is not just a dev toy — it ships a separate, hardened production stack and a local staging rehearsal that runs the identical image.
# Production (Traefik + Let's Encrypt TLS, multi-tenancy, tuned MariaDB, backups)
cp .env.prod.example .env.prod # set domains, email, passwords, registry
python entry.py prod build --preset example
python entry.py prod push # → your container registry
python entry.py prod up
python entry.py prod new-site erp.example.com
# Staging — the SAME image locally, Traefik off, on http://localhost:8080
python entry.py prod build --preset example
python entry.py staging up
python entry.py staging new-site erp.localhost
Full guides: PRODUCTION.md and STAGING.md.
Repository layout
frappe-devstack/
├── entry.py # CLI launcher (thin)
├── frappe_devstack/ # CLI package — all logic lives here
│ ├── cli.py # argparse command tree + dispatch
│ ├── wizard.py # first-run dev setup
│ ├── compose.py # dev lifecycle (build/stop/restart/destroy)
│ ├── config.py # env files, presets, ports, passwords
│ ├── images.py # image build (BuildKit secret, host clone)
│ ├── prod.py # production build/push/deploy/new-site
│ ├── staging.py # local staging
│ └── ui.py # terminal output helpers
├── Dockerfile # multi-stage frappe/build → frappe/base
├── docker-compose.yml # dev stack
├── docker-compose.prod.yml # standalone production stack
├── docker-compose.staging.yml # staging overlay on the prod stack
├── presets/ # app sets (example.json, private-app.example.json)
├── .env.example # dev env keys (.env is generated)
├── .env.secrets.example # GITHUB_PAT template
├── .env.prod.example # production env template
├── .gitlab-ci.yml # GitLab pipeline (build + push image)
└── .github/workflows/ci.yml # GitHub Actions (lint + validate config)
CLI reference
```bash python entry.py [setup] # first-time dev bring-up (default) python entry.py setup --preset [--frappe-version version-16] [--port N] [--no-browser] python entry.py build --preset # rebuild dev image after editing a preset python entry.py stop | restart | destroy
python entry.py prod build --preset [--frappe-version version-16] python entry.py prod push python entry.py prod up | down python entry.py prod new-site
Related Developer Tools apps for Frappe & ERPNext
- Frappe — Low code web framework for real world applications, in Python and Javascript
- Frappe Docker — Docker environment for developing, deploying, and running Frappe applications (ERPNext and custom apps) in production and development
- Builder — Craft beautiful websites effortlessly with an intuitive visual builder and publish them instantly
- Bench — CLI to manage Multi-tenant deployments for Frappe apps
- Frappe Ui — A set of components and utilities for rapid UI development
- Press — Full service cloud hosting for the Frappe stack - powers Frappe Cloud
- Gameplan — Open Source Discussions Platform for Remote Teams
- Doppio — A Frappe app (CLI) to magically setup single page applications and Vue/React powered desk pages on your custom Frappe apps.