Compare commits

...

55 commits
v3.2.0 ... main

Author SHA1 Message Date
github-actions
825536a998 chore: Update example.env
Some checks are pending
Core / Build Stable / v15_test (push) Waiting to run
Core / Build Stable / v15_publish (push) Blocked by required conditions
Core / Build Stable / v16_test (push) Waiting to run
Core / Build Stable / v16_publish (push) Blocked by required conditions
Core / Build Stable / Update example.env and pwd.yml (push) Blocked by required conditions
Core / Build Stable / Release Helm (push) Blocked by required conditions
Lint / lint (push) Waiting to run
2026-06-16 22:05:53 +00:00
Daniel Radl
91fc59a134
Merge pull request #1926 from frappe/dependabot/pip/pytest-9.1.0
chore(deps): bump pytest from 9.0.3 to 9.1.0
2026-06-15 11:00:41 +02:00
dependabot[bot]
f5eae9dffc
chore(deps): bump pytest from 9.0.3 to 9.1.0
Bumps [pytest](https://github.com/pytest-dev/pytest) from 9.0.3 to 9.1.0.
- [Release notes](https://github.com/pytest-dev/pytest/releases)
- [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest/compare/9.0.3...9.1.0)

---
updated-dependencies:
- dependency-name: pytest
  dependency-version: 9.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-15 08:25:30 +00:00
github-actions
7ef1d3e664 chore: Update example.env 2026-06-10 00:52:31 +00:00
Daniel Radl
55cbf8e31f
Merge pull request #1919 from AMR-Mannesmann/chore(devcontainer)add-opinionated-extensions
chore(devcontainer):add opinionated extensions
2026-06-09 13:54:47 +02:00
Daniel Radl
a1d4499fae
Merge branch 'frappe:main' into chore(devcontainer)add-opinionated-extensions 2026-06-08 13:27:23 +02:00
Daniel Radl
1e3d40fa65
Merge pull request #1920 from AMR-Mannesmann/fix(docs)EOF
fix(docs):EOF
2026-06-08 12:45:46 +02:00
Daniel Radl
f60da9b964
fix(docs):EOF 2026-06-08 12:40:20 +02:00
Daniel Radl
25477c9e08
chore(devcontainer):add opinionated extensions 2026-06-08 12:27:53 +02:00
github-actions
b7a0badaf0 chore: Update example.env 2026-06-03 10:09:24 +00:00
github-actions
3fccc2f9e7 chore: Update example.env 2026-06-02 17:24:35 +00:00
github-actions
6fccccf6d1 chore: Update example.env 2026-06-01 06:39:33 +00:00
github-actions
6526ab8cd4 chore: Update example.env 2026-05-27 01:51:34 +00:00
Daniel Radl
c7da80ea3f
Merge pull request #1913 from frappe/dependabot/github_actions/docker/bake-action-7.2.0
chore(deps): bump docker/bake-action from 7.1.0 to 7.2.0
2026-05-26 15:45:57 +02:00
Daniel Radl
4e5a578801
Merge pull request #1917 from AMR-Mannesmann/docs(ref)-add_assets_doc
docs(ref): add assets doc
2026-05-26 15:42:09 +02:00
Daniel Radl
6695bb7b03 docs(ref): add assets doc 2026-05-26 15:40:20 +02:00
Daniel Radl
8c47ee9eb5
Merge pull request #1916 from iragca/main
docs(example): correct app installation command for crm subdomain in …
2026-05-26 14:25:28 +02:00
Chris Irag
6377a34c61 docs(example): correct app installation command for crm subdomain in nginx proxy setup 2026-05-26 19:42:21 +08:00
RocketQuack
e64251a86a
Merge pull request #1914 from fredol/fix-arm64-doc
Fix Documentation and bump ERPNext version to v16.19.1
2026-05-26 11:46:14 +02:00
Frederic Ollivier
cb0eda5214 Change ERPNext version to v16.19.1
Fix typo in platform, arm64 instead of amd64
2026-05-23 16:46:29 +02:00
dependabot[bot]
0cb8df1631
chore(deps): bump docker/bake-action from 7.1.0 to 7.2.0
Bumps [docker/bake-action](https://github.com/docker/bake-action) from 7.1.0 to 7.2.0.
- [Release notes](https://github.com/docker/bake-action/releases)
- [Commits](https://github.com/docker/bake-action/compare/v7.1.0...v7.2.0)

---
updated-dependencies:
- dependency-name: docker/bake-action
  dependency-version: 7.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-22 08:36:36 +00:00
Daniel Radl
930d44603a
Merge pull request #1912 from frappe/dependabot/npm_and_yarn/docs/postcss-8.5.15
chore(deps): bump postcss from 8.5.8 to 8.5.15 in /docs
2026-05-21 15:14:06 +02:00
dependabot[bot]
08d765c413
chore(deps): bump postcss from 8.5.8 to 8.5.15 in /docs
Bumps [postcss](https://github.com/postcss/postcss) from 8.5.8 to 8.5.15.
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/postcss/compare/8.5.8...8.5.15)

---
updated-dependencies:
- dependency-name: postcss
  dependency-version: 8.5.15
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-21 13:01:31 +00:00
Daniel Radl
c710eef89e
Merge pull request #1911 from AMR-Mannesmann/feat(actions)add_exept_issue_label
feat(actions):add exept issue label
2026-05-21 14:59:40 +02:00
Daniel Radl
33f24b2645
feat(actions):add exept issue label 2026-05-21 14:54:46 +02:00
github-actions
640b761a3d chore: Update example.env 2026-05-20 07:50:30 +00:00
Daniel Radl
73b029945b
Merge pull request #1909 from harshith-ashok/docs/add_docker_dev_guide
docs(contributing): add docker dev guide
2026-05-20 09:34:37 +02:00
github-actions
38ca8d2316 chore: Update example.env 2026-05-20 04:40:07 +00:00
Harshith Ashok
48764b21c1
remove mariadb version for Frappe Framework setup 2026-05-19 19:55:03 +05:30
Daniel Radl
19259e9d02
Merge pull request #1896 from ASATechnologies/daniel/configure_gunicorn
feat: configure gunicorn with env variables
2026-05-19 15:00:22 +02:00
Harshith Ashok
6e1117bbbc removed old file and replaced it under development 2026-05-19 00:22:22 +05:30
dandax123
2c44349a0f feat: configure gunicorn with env variables 2026-05-18 20:19:43 +02:00
Harshith Ashok
5097115d08
Update setup.md with mariadb version change advice
Added note about mariadb version causing errors. This addition is based on Issue #1908 which I too faced while testing and setting it up myself.
2026-05-17 09:26:23 +05:30
Harshith Ashok
cfd280eff3
Clarify mariadb version requirement in setup
Add note about mariadb version for Frappe Framework.
2026-05-17 09:21:54 +05:30
Harshith Ashok
aafc25bc76 docs(contributing): add docker dev guide 2026-05-17 07:45:04 +05:30
github-actions
e31bcceac1 chore: Update example.env 2026-05-14 10:07:39 +00:00
github-actions
eeb487e5e6 chore: Update example.env 2026-05-14 06:04:47 +00:00
github-actions
cec5b93546 chore: Update example.env 2026-05-13 10:46:13 +00:00
github-actions
d07d805436 chore: Update example.env 2026-05-12 19:20:13 +00:00
RocketQuack
5abd3c0f95
Merge pull request #1903 from oktett-8/Fix-entrypoint-permission
Fix entrypoint.sh permission
2026-05-09 17:25:21 +02:00
Ingo Schuck
004b27a5a7 Reapply "fix all entrypoint.sh permissions to 755"
This reverts commit 93ade44c6b.
2026-05-08 16:41:30 +02:00
Ingo Schuck
37e91a2db2 Reapply "feat(images): permissive boolean check for INSTALL_CHROMIUM"
This reverts commit 8f4130b5d3.
2026-05-08 16:39:31 +02:00
Ingo Schuck
c363f459a4 Reapply "feat(images): toggle chromium installation"
This reverts commit 09fcd3e83b.
2026-05-08 16:38:52 +02:00
Ingo Schuck
09fcd3e83b Revert "feat(images): toggle chromium installation"
This reverts commit c302af9dd5.
2026-05-07 21:50:56 +02:00
Ingo Schuck
8f4130b5d3 Revert "feat(images): permissive boolean check for INSTALL_CHROMIUM"
This reverts commit 0a04e5ecd2.
2026-05-07 21:50:36 +02:00
Ingo Schuck
93ade44c6b Revert "fix all entrypoint.sh permissions to 755"
This reverts commit c7ac6b7666.
2026-05-07 21:50:12 +02:00
Oktett-8
f2d96ab8eb
Merge branch 'frappe:main' into Fix-entrypoint-permission 2026-05-07 21:46:16 +02:00
Ingo Schuck
c7ac6b7666 fix all entrypoint.sh permissions to 755 2026-05-07 21:40:40 +02:00
jslocomotor
0a04e5ecd2 feat(images): permissive boolean check for INSTALL_CHROMIUM 2026-05-07 21:40:40 +02:00
jslocomotor
c302af9dd5 feat(images): toggle chromium installation 2026-05-07 21:40:40 +02:00
Daniel Radl
d24093469d
Merge pull request #1905 from jslocomotor/feat/configure-chromium-installation
feat(images): toggle chromium installation
2026-05-07 15:11:26 +02:00
jslocomotor
2af7b06f8d feat(images): permissive boolean check for INSTALL_CHROMIUM 2026-05-07 14:03:12 +02:00
jslocomotor
5d9f2e41a0 feat(images): toggle chromium installation 2026-05-06 20:30:52 +02:00
Ingo Schuck
373e6c1e20 Fix entrypoint.sh permission 2026-05-06 19:35:03 +02:00
github-actions
edfd8f0755 chore: Update example.env 2026-05-05 17:03:26 +00:00
22 changed files with 452 additions and 95 deletions

View file

@ -38,7 +38,7 @@ jobs:
run: echo "LATEST_BENCH_RELEASE=$(curl -s 'https://api.github.com/repos/frappe/bench/releases/latest' | jq -r '.tag_name')" >> "$GITHUB_ENV"
- name: Build and test
uses: docker/bake-action@v7.1.0
uses: docker/bake-action@v7.2.0
with:
source: .
targets: bench-test
@ -52,7 +52,7 @@ jobs:
- name: Push
if: ${{ github.repository == 'frappe/frappe_docker' && github.event_name != 'pull_request' }}
uses: docker/bake-action@v7.1.0
uses: docker/bake-action@v7.2.0
with:
targets: bench
push: true

View file

@ -87,7 +87,7 @@ jobs:
echo "NODE_VERSION=${{ inputs.node_version }}" >> "$GITHUB_ENV"
- name: Build
uses: docker/bake-action@v7.1.0
uses: docker/bake-action@v7.2.0
with:
source: .
push: true

View file

@ -70,7 +70,7 @@ jobs:
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Push Docker Hub images
uses: docker/bake-action@v7.1.0
uses: docker/bake-action@v7.2.0
with:
push: true
set: "*.platform=linux/amd64,linux/arm64"
@ -83,7 +83,7 @@ jobs:
password: ${{ secrets.GITHUB_TOKEN }}
- name: Push GHCR base images
uses: docker/bake-action@v7.1.0
uses: docker/bake-action@v7.2.0
with:
targets: base-images
push: true

View file

@ -16,3 +16,4 @@ jobs:
stale-pr-message: This PR has been automatically marked as stale. You have a week to explain why you believe this is an error.
stale-issue-label: no-issue-activity
stale-pr-label: no-pr-activity
exempt-issue-labels: keep-open

View file

@ -46,6 +46,10 @@ services:
backend:
<<: *backend_defaults
platform: linux/amd64
environment:
GUNICORN_THREADS: ${GUNICORN_THREADS:-4}
GUNICORN_WORKERS: ${GUNICORN_WORKERS:-2}
GUNICORN_TIMEOUT: ${GUNICORN_TIMEOUT:-120}
frontend:
<<: *customizable_image

View file

@ -10,7 +10,9 @@
"grapecity.gc-excelviewer",
"mtxr.sqltools",
"mtxr.sqltools-driver-mysql",
"visualstudioexptteam.vscodeintellicode"
"vue.volar",
"esbenp.prettier-vscode",
"charliermarsh.ruff"
],
"settings": {
"terminal.integrated.profiles.linux": {

View file

@ -35,11 +35,11 @@ This allows you to:
Installing apps into a running container is **not supported**.
`bench get-app` is an examples of an common but unsupported action.
`bench get-app` and `bench build` are examples of an common but unsupported actions.
### Why?
- Apps are part of the **Docker image**
- Apps and assets are part of the **Docker image**
- Runtime changes are lost on container recreation
- This ensures reproducibility and stability

View file

@ -29,8 +29,8 @@ here is the example pwd.yml file:
```yml
services:
backend:
image: frappe/erpnext:v15
platform: linux/amd64
image: frappe/erpnext:v16.19.1
platform: linux/arm64
deploy:
restart_policy:
condition: on-failure
@ -39,8 +39,8 @@ services:
- logs:/home/frappe/frappe-bench/logs
configurator:
image: frappe/erpnext:v15
platform: linux/amd64
image: frappe/erpnext:v16.19.1
platform: linux/arm64
deploy:
restart_policy:
condition: none
@ -68,8 +68,8 @@ services:
- logs:/home/frappe/frappe-bench/logs
create-site:
image: frappe/erpnext:v15
platform: linux/amd64
image: frappe/erpnext:v16.19.1
platform: linux/arm64
deploy:
restart_policy:
condition: none
@ -101,7 +101,7 @@ services:
db:
image: mariadb:11.8
platform: linux/amd64
platform: linux/arm64
healthcheck:
test: mysqladmin ping -h localhost --password=admin
interval: 1s
@ -119,8 +119,8 @@ services:
- db-data:/var/lib/mysql
frontend:
image: frappe/erpnext:v15
platform: linux/amd64
image: frappe/erpnext:v16.19.1
platform: linux/arm64
depends_on:
- websocket
deploy:
@ -144,8 +144,8 @@ services:
- "8080:8080"
queue-long:
image: frappe/erpnext:v15
platform: linux/amd64
image: frappe/erpnext:v16.19.1
platform: linux/arm64
deploy:
restart_policy:
condition: on-failure
@ -159,8 +159,8 @@ services:
- logs:/home/frappe/frappe-bench/logs
queue-short:
image: frappe/erpnext:v15
platform: linux/amd64
image: frappe/erpnext:v16.19.1
platform: linux/arm64
deploy:
restart_policy:
condition: on-failure
@ -175,7 +175,7 @@ services:
redis-queue:
image: redis:6.2-alpine
platform: linux/amd64
platform: linux/arm64
deploy:
restart_policy:
condition: on-failure
@ -184,14 +184,14 @@ services:
redis-cache:
image: redis:6.2-alpine
platform: linux/amd64
platform: linux/arm64
deploy:
restart_policy:
condition: on-failure
scheduler:
image: frappe/erpnext:v15
platform: linux/amd64
image: frappe/erpnext:v16.19.1
platform: linux/arm64
deploy:
restart_policy:
condition: on-failure
@ -203,8 +203,8 @@ services:
- logs:/home/frappe/frappe-bench/logs
websocket:
image: frappe/erpnext:v15
platform: linux/amd64
image: frappe/erpnext:v16.19.1
platform: linux/arm64
deploy:
restart_policy:
condition: on-failure

View file

@ -96,6 +96,7 @@ See [Automated Builds and Deployment](../03-production/06-automated-builds-and-d
| PYTHON_VERSION | Python version for the base image |
| NODE_VERSION | Node.js version |
| WKHTMLTOPDF_VERSION | wkhtmltopdf version |
| INSTALL_CHROMIUM | Configure chromium installation, defaults to `true` - needed for Frappe Workbench version >15 |
| **bench only** | |
| DEBIAN_BASE | Debian base version for the bench image, defaults to `bookworm` |
| WKHTMLTOPDF_DISTRO | use the specified distro for debian package. Default is `bookworm` |

View file

@ -122,6 +122,16 @@ If your site is named `example.com` and you access it via that domain, no need t
---
## Backend (Gunicorn) Configuration
| Variable | Purpose | Default | When to Set / Allowed Values |
| :----------------- | :------------------------------------------------------------- | :------ | :------------------------------------------------------------------------------- |
| `GUNICORN_WORKERS` | Number of worker processes handling web requests | `2` | Scale up for multi-core CPUs. Formula: `(2 x Cores) + 1` |
| `GUNICORN_THREADS` | Number of concurrent threads per worker process | `4` | Increase to handle more simultaneous I/O-bound requests without high memory cost |
| `GUNICORN_TIMEOUT` | Max time a worker can spend on a single request before restart | `120` | Increase if long-running reports or data imports time out |
---
## Frontend Nginx Configuration (inside the frontend container)
| Variable | Purpose | Default | Allowed Values |

View file

@ -149,7 +149,7 @@ docker compose --project-name erpnext exec backend \
# crm.your-domain.com
docker compose --project-name erpnext exec backend \
bench new-site --mariadb-user-host-login-scope=% --db-root-password changeit --install-app erpnext --admin-password changeit crm.your-domain.com
bench new-site --mariadb-user-host-login-scope=% --db-root-password changeit --install-app crm --admin-password changeit crm.your-domain.com
```
### Notes

View file

@ -0,0 +1,254 @@
---
title: Docker Development Setup
---
# Docker Development Setup
A complete guide for setting up a Frappe development environment on x86 and ARM based computers running UNIX based OSes by running containers directly and working inside them via the terminal. No VS Code Dev Containers extension needed.
> [!IMPORTANT]
> Devcontainers are the intended development setup for Frappe Framework but in case you don't want to use that method follow these instructions to use the CLI directly instead
---
## Prerequisites
- **Docker Desktop** (Applicable only for MacOS) — [download here](https://www.docker.com/products/docker-desktop/)
- **Git**
- A terminal (iTerm2, or the built-in Terminal.app)
### Docker Desktop Resource Allocation (Critical)
1. Open Docker Desktop → **Settings** → **Resources**
2. **Memory**: at least **6 GB** (8 GB recommended)
3. **CPUs**: at least **4**
4. **Disk image size**: at least **60 GB**
5. Click **Apply & Restart**
---
## Step 1 — Set ARM64 as Default Platform (ONLY FOR ARM BASED SYSTEMS)
```bash
export DOCKER_DEFAULT_PLATFORM=linux/arm64
```
Make it permanent:
```bash
echo 'export DOCKER_DEFAULT_PLATFORM=linux/arm64' >> ~/.zshrc
source ~/.zshrc
```
---
## Step 2 — Clone the Repo
```bash
git clone https://github.com/frappe/frappe_docker.git
cd frappe_docker
```
---
## Step 3 — Set Up the Dev Container Config
The `devcontainer-example/` folder contains a ready-made `docker-compose.yml` for development. Copy it into place:
```bash
cp -R devcontainer-example .devcontainer
```
This gives you `.devcontainer/docker-compose.yml` which defines all the services you need:
- `frappe` — the main development container (Debian, Python, Node, bench)
- `mariadb` — the database
- `redis-cache` — cache layer
- `redis-queue` — background job queue
---
## Step 4 — Add ARM64 Platform to All Services
Open `.devcontainer/docker-compose.yml` in any editor and add `platform: linux/arm64` to every service block. It should look like this:
```yaml
services:
frappe:
image: frappe/bench:latest
platform: linux/arm64
# ... rest of config
mariadb:
image: mariadb:10.8
platform: linux/arm64
# ...
redis-cache:
image: redis:6.2-alpine
platform: linux/arm64
# ...
redis-queue:
image: redis:6.2-alpine
platform: linux/arm64
# ...
```
> Without this, Docker may pull amd64 images and emulate them via Rosetta — things will work but be noticeably slower.
---
## Step 5 — Start the Containers
```bash
docker compose -f .devcontainer/docker-compose.yml up -d
```
Verify everything is running:
```bash
docker compose -f .devcontainer/docker-compose.yml ps
```
You should see all services with status `Up`.
In case you get any errors along the lines of,
```log
Error response from daemon: failed to set up container networking: driver failed programming external connectivity on endpoint devcontainer-frappe-1 (44b337b68d100e914fab0ce446ed08d791cc73aaffb05cf47c347c00ff88f567): Bind for 0.0.0.0:9001 failed: port is already allocated
```
- Check if the port is being used by another service with `lsof -i :PORT`
> Usually on MacOS ports 8000 and 9000 are usually reserved for system use
- Go to line 60 and 61 under the `frappe` service and change the ports
Eg:
```
ports:
- 8001-8005:8001-8005
- 9002-9005:9002-9005
```
---
## Step 6 — Enter the Development Container
```bash
docker exec -e "TERM=xterm-256color" -w /workspace/development -it devcontainer-frappe-1 bash
```
> The container name is typically `devcontainer-frappe-1`. If it differs, check with `docker ps` and use the actual name shown.
You are now inside the container as the `frappe` user. All subsequent commands in this guide run **inside the container** unless noted otherwise.
---
## Step 7 — Initialize a Bench
```bash
bench init --skip-redis-config-generation --frappe-branch version-16 frappe-bench
cd frappe-bench
```
Use `version-16` for the latest stable release. Swap for `version-15` if needed.
This creates:
```
development/
└── frappe-bench/
├── apps/ ← All Frappe apps live here
├── sites/ ← Your sites (databases, uploaded files)
├── env/ ← Python virtualenv
├── logs/
└── Procfile
```
---
## Step 8 — Configure Service Hosts
Tell bench to use the containerised services (not localhost):
```bash
bench set-config -g db_host mariadb
bench set-config -g redis_cache redis://redis-cache:6379
bench set-config -g redis_queue redis://redis-queue:6379
bench set-config -g redis_socketio redis://redis-queue:6379
```
If any command fails, edit the file directly:
```bash
nano sites/common_site_config.json
```
Paste:
```json
{
"db_host": "mariadb",
"redis_cache": "redis://redis-cache:6379",
"redis_queue": "redis://redis-queue:6379",
"redis_socketio": "redis://redis-queue:6379"
}
```
---
## Step 9 — Fix the Procfile
Redis runs in separate containers, so remove it from Honcho's Procfile to avoid conflicts:
```bash
sudo sed -i '/redis/d' ./Procfile
```
---
## Step 10 — Create a Site
```bash
bench new-site \
--db-root-password 123 \
--admin-password admin \
--mariadb-user-host-login-scope=% \
development.localhost
```
- MariaDB root password: `123` (set in the docker-compose defaults)
- Admin password: `admin` (change this to whatever you want)
- Site name **must end in `.localhost`**
---
## Step 11 — Enable Developer Mode
```bash
bench --site development.localhost set-config developer_mode 1
bench --site development.localhost clear-cache
```
---
## Step 12 — Add development.localhost to /etc/hosts (on your Mac)
Run this **on your Mac** (not inside the container):
```bash
echo "127.0.0.1 development.localhost" | sudo tee -a /etc/hosts
```
---
## Step 13 — Start the Dev Server
```bash
bench build # (optional)
bench start
```
Open your browser at **http://development.localhost:8000**
Login: `Administrator` / `admin`

View file

@ -0,0 +1,62 @@
---
title: How Assets are handled
---
# Assets Reference
## Problem
The `sites` directory contains both persistent data (site config, uploaded files, etc.) and build-time artifacts (`sites/assets`). Mounting the entire `sites` directory as a Docker volume causes assets to be persisted alongside config, which leads to:
- Stale assets surviving image updates
- Asset/manifest mismatches after rebuilds
- Assets being tied to the volume lifecycle rather than the image lifecycle
## Solution
Assets are moved out of the `sites` volume during the build process and replaced with a **symlink** later on. This means assets are always served from the image layer, while the rest of `sites` remains persistent.
### How it works
During the image build (`Containerfile`), the following is done:
```dockerfile
RUN cp -r /home/frappe/frappe-bench/sites/assets /home/frappe/frappe-bench/assets && \
rm -rf /home/frappe/frappe-bench/sites/assets
```
This runs **before** the `VOLUME` declaration, so the **`sites` volume does not contain any assets at all**.
Additionally an `ENTRYPOINT` is added to the images which adds a **symlink** from `assets` to `site\assets`.
> This is implemented in the entrypoint instead of baking the symlink directly into the image so it also works with pre-existing or already-initialized `sites` volumes.
> Since mounting a volume over `/home/frappe/frappe-bench/sites` hides the image contents at that path, any symlink created during the image build would not be visible inside the mounted volume. The entrypoint recreates the symlink at container startup, ensuring it always exists and automatically repairing older volumes that may not already contain it.
At runtime:
```
/home/frappe/frappe-bench/
├── assets/ ← image layer (ephemeral, always matches the image)
├── sites/
│ ├── assets -> /home/frappe/frappe-bench/assets ← symlink
│ ├── common_site_config.json ← persisted in volume
│ └── <site>/ ← persisted in volume
└── logs/ ← persisted in volume
```
### Volume behavior
| Path | Persistent | Source |
| -------------------------- | ----------------------- | ---------------------- |
| `sites/` (except assets) | ✅ Yes | Named volume (`sites`) |
| `sites/assets` (symlink) | ✅ Yes (symlink itself) | Named volume (`sites`) |
| `assets/` (symlink target) | ❌ No | Image layer |
| `logs/` | ✅ Yes | Unnamed volume |
The `sites/assets` symlink is stored inside the persistent `sites` volume, but its target (`/home/frappe/frappe-bench/assets`) comes from the container image. When the container is recreated or upgraded, the assets directory is recreated from the new image, ensuring assets always stay in sync with the running version.
## Important: `bench build` at runtime
Running `bench build` inside a running container will write new assets and eventually cause a mismatch between `assets.json` and the actual assets, breaking the UI. This can be recovered by recreating the containers
> Note: restarting the containers is not sufficient — they need to be recreated to discard the writable layer.

View file

@ -15,7 +15,7 @@ importers:
devDependencies:
vitepress:
specifier: 2.0.0-alpha.16
version: 2.0.0-alpha.16(postcss@8.5.8)
version: 2.0.0-alpha.16(postcss@8.5.15)
vitepress-sidebar:
specifier: 1.33.1
version: 1.33.1
@ -408,6 +408,7 @@ packages:
'@ungap/structured-clone@1.3.0':
resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==}
deprecated: Potential CWE-502 - Update to 1.3.1 or higher
'@vitejs/plugin-vue@6.0.5':
resolution: {integrity: sha512-bL3AxKuQySfk1iGcBsQnoRVexTPJq0Z/ixFVM8OhVJAP6ZXXXLtM7NFKWhLl30Kg7uTBqIaPXbh+nuQCuBDedg==}
@ -707,8 +708,8 @@ packages:
minisearch@7.2.0:
resolution: {integrity: sha512-dqT2XBYUOZOiC5t2HRnwADjhNS2cecp9u+TJRiJ1Qp/f5qjkeT5APcGPjHw+bz89Ms8Jp+cG4AlE+QZ/QnDglg==}
nanoid@3.3.11:
resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
nanoid@3.3.12:
resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
hasBin: true
@ -739,8 +740,8 @@ packages:
resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==}
engines: {node: '>=12'}
postcss@8.5.8:
resolution: {integrity: sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==}
postcss@8.5.15:
resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==}
engines: {node: ^10 || ^12 || >=14}
property-information@7.1.0:
@ -1214,7 +1215,7 @@ snapshots:
'@vue/shared': 3.5.30
estree-walker: 2.0.2
magic-string: 0.30.21
postcss: 8.5.8
postcss: 8.5.15
source-map-js: 1.2.1
'@vue/compiler-ssr@3.5.30':
@ -1491,7 +1492,7 @@ snapshots:
minisearch@7.2.0: {}
nanoid@3.3.11: {}
nanoid@3.3.12: {}
oniguruma-parser@0.12.1: {}
@ -1516,9 +1517,9 @@ snapshots:
picomatch@4.0.4: {}
postcss@8.5.8:
postcss@8.5.15:
dependencies:
nanoid: 3.3.11
nanoid: 3.3.12
picocolors: 1.1.1
source-map-js: 1.2.1
@ -1671,7 +1672,7 @@ snapshots:
esbuild: 0.27.4
fdir: 6.5.0(picomatch@4.0.4)
picomatch: 4.0.4
postcss: 8.5.8
postcss: 8.5.15
rollup: 4.59.0
tinyglobby: 0.2.15
optionalDependencies:
@ -1683,7 +1684,7 @@ snapshots:
gray-matter: 4.0.3
qsu: 1.10.4
vitepress@2.0.0-alpha.16(postcss@8.5.8):
vitepress@2.0.0-alpha.16(postcss@8.5.15):
dependencies:
'@docsearch/css': 4.6.0
'@docsearch/js': 4.6.0
@ -1705,7 +1706,7 @@ snapshots:
vite: 7.3.2
vue: 3.5.30
optionalDependencies:
postcss: 8.5.8
postcss: 8.5.15
transitivePeerDependencies:
- '@types/node'
- async-validator

View file

@ -1,6 +1,6 @@
# Reference: https://github.com/frappe/frappe_docker/blob/main/docs/02-setup/04-env-variables.md
ERPNEXT_VERSION=v16.16.0
ERPNEXT_VERSION=v16.23.0
DB_PASSWORD=123
@ -15,6 +15,17 @@ DB_PORT=
REDIS_CACHE=
REDIS_QUEUE=
# The number of threads per Gunicorn worker process for handling concurrent requests.
GUNICORN_THREADS=4
# The number of worker processes for handling requests.
# A typical formula is (2 x number of CPU cores) + 1.
GUNICORN_WORKERS=2
# Workers exceeding this timeout (in seconds) will be killed and restarted.
GUNICORN_TIMEOUT=120
# Only with HTTPS override
LETSENCRYPT_EMAIL=mail@example.com

View file

@ -4,6 +4,7 @@ LABEL author=frappé
ARG GIT_REPO=https://github.com/frappe/bench.git
ARG GIT_BRANCH=v5.x
ARG INSTALL_CHROMIUM=true
RUN apt-get update \
&& DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y \
@ -23,8 +24,6 @@ RUN apt-get update \
libharfbuzz0b \
libpangoft2-1.0-0 \
libpangocairo-1.0-0 \
#Chromium
chromium-headless-shell \
# to work inside the container
locales \
build-essential \
@ -75,6 +74,11 @@ RUN apt-get update \
file \
# For MIME type detection
media-types \
# Chromium
&& if [ "$INSTALL_CHROMIUM" != "false" ]; then \
DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y \
chromium-headless-shell; \
fi \
&& rm -rf /var/lib/apt/lists/*
RUN sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen \

View file

@ -8,6 +8,8 @@ COPY resources/core/nginx/security_headers.conf /etc/nginx/snippets/security_hea
ARG WKHTMLTOPDF_VERSION=0.12.6.1-3
ARG WKHTMLTOPDF_DISTRO=bookworm
ARG INSTALL_CHROMIUM=true
ARG NODE_VERSION=24.13.0
ENV NVM_DIR=/home/frappe/.nvm
ENV PATH=${NVM_DIR}/versions/node/v${NODE_VERSION}/bin/:${PATH}
@ -26,8 +28,6 @@ RUN useradd -ms /bin/bash frappe \
libharfbuzz0b \
libpangoft2-1.0-0 \
libpangocairo-1.0-0 \
#Chromium
chromium-headless-shell \
# For backups
restic \
gpg \
@ -62,6 +62,11 @@ RUN useradd -ms /bin/bash frappe \
&& curl -sLO https://github.com/wkhtmltopdf/packaging/releases/download/$WKHTMLTOPDF_VERSION/$downloaded_file \
&& apt-get install -y ./$downloaded_file \
&& rm $downloaded_file \
# Chromium
&& if [ "$INSTALL_CHROMIUM" != "false" ]; then \
DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y \
chromium-headless-shell; \
fi \
# Clean up
&& rm -rf /var/lib/apt/lists/* \
&& rm -fr /etc/nginx/sites-enabled/default \
@ -158,20 +163,12 @@ VOLUME [ \
USER root
# This entrypoint script link build assets of the image to the mounted sites volume at container initialization
COPY resources/core/main-entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod +x /usr/local/bin/entrypoint.sh
RUN chmod 755 /usr/local/bin/entrypoint.sh
COPY resources/core/start.sh /usr/local/bin/start.sh
RUN chmod 755 /usr/local/bin/start.sh
USER frappe
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
CMD [ \
"/home/frappe/frappe-bench/env/bin/gunicorn", \
"--chdir=/home/frappe/frappe-bench/sites", \
"--bind=0.0.0.0:8000", \
"--threads=4", \
"--workers=2", \
"--worker-class=gthread", \
"--worker-tmp-dir=/dev/shm", \
"--timeout=120", \
"--preload", \
"frappe.app:application" \
]
CMD ["start.sh"]

View file

@ -47,20 +47,12 @@ VOLUME [ \
USER root
# This entrypoint script link build assets of the image to the mounted sites volume at container initialization
COPY resources/core/main-entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod +x /usr/local/bin/entrypoint.sh
RUN chmod 755 /usr/local/bin/entrypoint.sh
COPY resources/core/start.sh /usr/local/bin/start.sh
RUN chmod 755 /usr/local/bin/start.sh
USER frappe
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
CMD [ \
"/home/frappe/frappe-bench/env/bin/gunicorn", \
"--chdir=/home/frappe/frappe-bench/sites", \
"--bind=0.0.0.0:8000", \
"--threads=4", \
"--workers=2", \
"--worker-class=gthread", \
"--worker-tmp-dir=/dev/shm", \
"--timeout=120", \
"--preload", \
"frappe.app:application" \
]
CMD ["start.sh"]

View file

@ -4,6 +4,8 @@ FROM python:${PYTHON_VERSION}-slim-${DEBIAN_BASE} AS base
ARG WKHTMLTOPDF_VERSION=0.12.6.1-3
ARG WKHTMLTOPDF_DISTRO=bookworm
ARG INSTALL_CHROMIUM=true
ARG NODE_VERSION=24.13.0
ENV NVM_DIR=/home/frappe/.nvm
ENV PATH=${NVM_DIR}/versions/node/v${NODE_VERSION}/bin/:${PATH}
@ -22,8 +24,6 @@ RUN useradd -ms /bin/bash frappe \
libharfbuzz0b \
libpangoft2-1.0-0 \
libpangocairo-1.0-0 \
#Chromium
chromium-headless-shell \
# For backups
restic \
gpg \
@ -58,6 +58,11 @@ RUN useradd -ms /bin/bash frappe \
&& curl -sLO https://github.com/wkhtmltopdf/packaging/releases/download/$WKHTMLTOPDF_VERSION/$downloaded_file \
&& apt-get install -y ./$downloaded_file \
&& rm $downloaded_file \
# Chromium
&& if [ "$INSTALL_CHROMIUM" != "false" ]; then \
DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y \
chromium-headless-shell; \
fi \
# Clean up
&& rm -rf /var/lib/apt/lists/* \
&& rm -fr /etc/nginx/sites-enabled/default \
@ -77,6 +82,7 @@ RUN useradd -ms /bin/bash frappe \
COPY resources/core/nginx/nginx-template.conf /templates/nginx/frappe.conf.template
COPY resources/core/nginx/nginx-entrypoint.sh /usr/local/bin/nginx-entrypoint.sh
COPY resources/core/nginx/security_headers.conf /etc/nginx/snippets/security_headers.conf
RUN chmod 755 /usr/local/bin/nginx-entrypoint.sh
FROM base AS build
@ -148,20 +154,12 @@ VOLUME [ \
USER root
# This entrypoint script link build assets of the image to the mounted sites volume at container initialization
COPY resources/core/main-entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod +x /usr/local/bin/entrypoint.sh
RUN chmod 755 /usr/local/bin/entrypoint.sh
COPY resources/core/start.sh /usr/local/bin/start.sh
RUN chmod 755 /usr/local/bin/start.sh
USER frappe
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
CMD [ \
"/home/frappe/frappe-bench/env/bin/gunicorn", \
"--chdir=/home/frappe/frappe-bench/sites", \
"--bind=0.0.0.0:8000", \
"--threads=4", \
"--workers=2", \
"--worker-class=gthread", \
"--worker-tmp-dir=/dev/shm", \
"--timeout=120", \
"--preload", \
"frappe.app:application" \
]
CMD ["start.sh"]

16
pwd.yml
View file

@ -1,6 +1,6 @@
services:
backend:
image: frappe/erpnext:v16.16.0
image: frappe/erpnext:v16.23.0
networks:
- frappe_network
deploy:
@ -16,7 +16,7 @@ services:
MARIADB_ROOT_PASSWORD: admin
configurator:
image: frappe/erpnext:v16.16.0
image: frappe/erpnext:v16.23.0
networks:
- frappe_network
deploy:
@ -45,7 +45,7 @@ services:
- logs:/home/frappe/frappe-bench/logs
create-site:
image: frappe/erpnext:v16.16.0
image: frappe/erpnext:v16.23.0
networks:
- frappe_network
deploy:
@ -101,7 +101,7 @@ services:
- db-data:/var/lib/mysql
frontend:
image: frappe/erpnext:v16.16.0
image: frappe/erpnext:v16.23.0
networks:
- frappe_network
depends_on:
@ -127,7 +127,7 @@ services:
- "8080:8080"
queue-long:
image: frappe/erpnext:v16.16.0
image: frappe/erpnext:v16.23.0
networks:
- frappe_network
deploy:
@ -146,7 +146,7 @@ services:
FRAPPE_REDIS_QUEUE: redis://redis-queue:6379
queue-short:
image: frappe/erpnext:v16.16.0
image: frappe/erpnext:v16.23.0
networks:
- frappe_network
deploy:
@ -183,7 +183,7 @@ services:
condition: on-failure
scheduler:
image: frappe/erpnext:v16.16.0
image: frappe/erpnext:v16.23.0
networks:
- frappe_network
deploy:
@ -197,7 +197,7 @@ services:
- logs:/home/frappe/frappe-bench/logs
websocket:
image: frappe/erpnext:v16.16.0
image: frappe/erpnext:v16.23.0
networks:
- frappe_network
deploy:

View file

@ -1 +1 @@
pytest==9.0.3
pytest==9.1.0

20
resources/core/start.sh Executable file
View file

@ -0,0 +1,20 @@
#!/bin/bash
set -e
#Gunicorn defaults
GUNICORN_THREADS=${GUNICORN_THREADS:-4}
GUNICORN_WORKERS=${GUNICORN_WORKERS:-2}
GUNICORN_TIMEOUT=${GUNICORN_TIMEOUT:-120}
echo "Booting Gunicorn with $GUNICORN_WORKERS workers and $GUNICORN_THREADS threads..."
exec /home/frappe/frappe-bench/env/bin/gunicorn \
--chdir=/home/frappe/frappe-bench/sites \
--bind=0.0.0.0:8000 \
--threads="$GUNICORN_THREADS" \
--workers="$GUNICORN_WORKERS" \
--worker-class=gthread \
--worker-tmp-dir=/dev/shm \
--timeout="$GUNICORN_TIMEOUT" \
--preload \
frappe.app:application