Compare commits

..

255 commits
v2.0.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
RocketQuack
d8393e0402
Merge pull request #1897 from jslocomotor/feat/migrator-service
feat(compose): add migrator service override and documentation
2026-05-03 23:32:20 +02:00
jslocomotor
9432daaaaf docs(env): added migration service variable description 2026-05-03 21:06:04 +02:00
jslocomotor
092a3769b1 docs(overrides): corrected migrator description 2026-04-30 23:33:05 +02:00
jslocomotor
ae221ebf7a feat(migrator): add multi-site support to migration 2026-04-30 23:15:58 +02:00
jslocomotor
815946194c
Merge branch 'frappe:main' into feat/migrator-service 2026-04-30 21:27:18 +02:00
RocketQuack
5cdd428a66
Merge pull request #1894 from ASATechnologies/daniel/fix_ci_and_variable_threads
fix(assets): auto-repair sites assets link on startup
2026-04-30 16:53:46 +02:00
dandax123
64e6536592 fix(assets): link assets at container init 2026-04-30 13:32:25 +02:00
jslocomotor
c48aa7f39b
Merge branch 'frappe:main' into feat/migrator-service 2026-04-29 19:01:23 +02:00
jslocomotor
12e6e821bc feat(compose): add migrator service override and documentation 2026-04-29 18:57:49 +02:00
github-actions
473f08a7f9 chore: Update example.env 2026-04-28 21:31:13 +00:00
RocketQuack
db21f966ef
Merge pull request #1892 from jslocomotor/fix/docker-build-cache-apps-json
fix(build): invalidate cache when apps.json changes
2026-04-27 22:06:28 +02:00
jslocomotor
0b835b0819 docs(production): add reference to CACHE_BUST explanation 2026-04-27 21:56:00 +02:00
jslocomotor
8428dfe9ba docs(production): add automated builds and deployment guide 2026-04-27 21:37:16 +02:00
jslocomotor
a260d9a431 fix(build): add optional CACHE_BUST for custom image rebuilds 2026-04-25 17:06:20 +02:00
jslocomotor
84e0608209
Merge branch 'frappe:main' into fix/docker-build-cache-apps-json 2026-04-25 15:41:12 +02:00
github-actions
007ae42d2f chore: Update example.env 2026-04-24 16:24:36 +00:00
RocketQuack
4b8d6a94cd
Merge pull request #1891 from Rocket-Quack/fix/workflow-update-env-permissions
fix(workflows): allow stable env update push
2026-04-24 17:54:23 +02:00
RocketQuack
71399ec0f3 fix(workflows): allow stable env update push 2026-04-24 17:29:39 +02:00
jslocomotor
c7a437ad63
Merge branch 'frappe:main' into fix/docker-build-cache-apps-json 2026-04-24 17:12:12 +02:00
jslocomotor
d2308438d0 fix(build): add apps.json hash for cache invalidation 2026-04-24 17:11:06 +02:00
RocketQuack
50e0ad5d3f
Merge pull request #1890 from Rocket-Quack/fix/reusable-core-build-workflows
fix(workflows): enable reusable core build workflows
2026-04-24 16:22:53 +02:00
RocketQuack
6633e61bfb fix(workflows): enable reusable core build workflows 2026-04-24 16:08:26 +02:00
RocketQuack
f8cfe4cb82
Merge pull request #1886 from frappe/dependabot/github_actions/docker/build-push-action-7
chore(deps): bump docker/build-push-action from 6 to 7
2026-04-23 11:55:44 +02:00
dependabot[bot]
a41764b9cc
chore(deps): bump docker/build-push-action from 6 to 7
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6 to 7.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v6...v7)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-23 08:23:59 +00:00
RocketQuack
b358359f61
Merge pull request #1880 from ews-pgasser/chore/bump-redis-socketio-6.2-to-8.6
chore: bump redis from 6.2 to 8.6-alpine
2026-04-22 17:46:32 +02:00
Daniel Radl
50ab0b7503
Merge pull request #1885 from AMR-Mannesmann/docsadd-title-06.03
fix(docs): add title to 06.03
2026-04-22 16:40:39 +02:00
Daniel Radl
dabfb42777
fix(docs): add title to 06.03 2026-04-22 16:38:55 +02:00
Daniel Radl
91bde56e7b
Merge pull request #1882 from simonsan/patch-1
Use `mariadb-admin` for health check
2026-04-22 16:33:31 +02:00
Daniel Radl
05dfe8912f
fix(pwd): use mariadb healthcheck.sh 2026-04-22 16:31:19 +02:00
RocketQuack
68a8ed8a84 docs(readme): refresh README header and overview 2026-04-22 15:28:45 +02:00
RocketQuack
9a345364e4 docs(readme): remove duplicate documentation links 2026-04-22 15:28:39 +02:00
RocketQuack
bcdd8127c0 docs(readme): clarify demo setup and remove play-with-docker link because no longer available 2026-04-22 15:28:39 +02:00
RocketQuack
3b3873cad5
Merge branch 'frappe:main' into main 2026-04-22 12:30:28 +02:00
RocketQuack
787eed965d
Merge pull request #1881 from Rocket-Quack/feat/rework-workflows-image-building
feat: rework workflows and new image building workflow
2026-04-22 12:24:42 +02:00
RocketQuack
68aca626f7
Merge pull request #1808 from Bamboi-tech/fix/add-pnpm
fix: enable pnpm via corepack for apps that require it (e.g. drive)
2026-04-22 12:22:46 +02:00
Thomas
ef3eba6ac9
enable pnpm via corepack in prod containerfil 2026-04-22 11:44:03 +02:00
github-actions
2fb6f2553d chore: Update example.env 2026-04-22 00:48:17 +00:00
RocketQuack
de4c85f68f chore(ci): add compatibility wrappers for legacy workflow names 2026-04-22 00:07:57 +02:00
simonsan
e72767546f
Use mariadb-admin for health check
mysql_upgrade is not existing anymore in the new 11.x images
2026-04-21 23:49:18 +02:00
RocketQuack
84a48c65eb chore(ci): restore upstream defaults after fork validation 2026-04-21 19:40:08 +02:00
RocketQuack
0d11308944
Merge pull request #2 from Rocket-Quack/feat/rework-workflows-image-building
chore(ci): allow image publishing on test fork
2026-04-21 19:28:53 +02:00
RocketQuack
3024cd132d chore(ci): allow image publishing on test fork 2026-04-21 19:27:39 +02:00
RocketQuack
a0bef9c2db
Merge pull request #1 from Rocket-Quack/feat/rework-workflows-image-building
Feat/rework workflows image building
2026-04-21 19:11:36 +02:00
RocketQuack
fec3af20cd docs(ci): document current image workflow setup 2026-04-21 19:05:23 +02:00
RocketQuack
01af0df21d feat(ci): add reusable workflow for downstream app images 2026-04-21 19:05:14 +02:00
RocketQuack
0281722f75 feat(ci): split core image workflows and publish base images to ghcr 2026-04-21 19:05:00 +02:00
ews-pgasser
960a3732ce chore: bump redis from 6.2 to 8.6-alpine 2026-04-21 09:05:34 +02:00
RocketQuack
28dbfd57ba
Merge pull request #1879 from ews-pgasser/fix/remove-nested-sites-assets-volume
Fix/remove nested sites assets volume
2026-04-20 17:50:17 +02:00
ews-pgasser
17670ec04c docs: move sites/assets volume upgrade note to migration docs 2026-04-20 17:23:46 +02:00
ews-pgasser
63f5169610 fix: removed sites/assets volume from custom & production Containerfile too 2026-04-20 16:30:58 +02:00
ews-pgasser
0cddb6f35b docs: document volume migration notes for sites/assets change 2026-04-20 15:51:19 +02:00
ews-pgasser
9ae6989269 fix: remove nested sites assets volume 2026-04-20 15:34:55 +02:00
Daniel Radl
d899df9d8a
Merge pull request #1876 from jslocomotor/fix/upgrade-mariadb-11-8
chore(compose): use MariaDB 11.8 and remove obsolete 10.6 workaround
2026-04-20 12:25:06 +02:00
jslocomotor
9cecbc7b2d chore(compose): use MariaDB 11.8 and remove obsolete MariaDB 10.6 workaround 2026-04-18 21:18:22 +02:00
Daniel Radl
a1737ea62d
Merge pull request #1861 from OmarElaraby26/fix/apps-json-token-leak
fix(security): replace APPS_JSON_BASE64 build-arg with BuildKit secret
2026-04-15 14:25:35 +02:00
RocketQuack
d33890a905
Merge pull request #1873 from AMR-Mannesmann/chore(vscode)/show-git-folder
chore(vscode): show git folder
2026-04-15 14:14:07 +02:00
RocketQuack
4e5f84fa29 chore: remove comments about why BuildKit is being used to parse apps.json 2026-04-15 13:52:31 +02:00
Daniel Radl
1fe7523bfb
chore(vscode): show git folder 2026-04-15 12:44:03 +02:00
github-actions
616ffd4177 chore: Update example.env 2026-04-14 18:46:14 +00:00
RocketQuack
159eb8231f
Merge pull request #1872 from Rocket-Quack/fix/docs-pipeline-1
ci(docs): switch pages workflow to corepack-managed pnpm
2026-04-14 14:09:40 +02:00
RocketQuack
adc72561a1 ci(docs): remove setup-node pnpm cache for corepack flow 2026-04-14 14:04:05 +02:00
RocketQuack
1d95762815 ci(docs): switch pages workflow to corepack-managed pnpm 2026-04-14 14:02:04 +02:00
RocketQuack
0feb49d00a ci(docs): add lockfile debug output for pages workflow 2026-04-14 13:55:29 +02:00
RocketQuack
169d5be00c ci(docs): pin pnpm version for pages build 2026-04-14 13:49:21 +02:00
RocketQuack
f55133966b
Merge pull request #1869 from frappe/dependabot/github_actions/pnpm/action-setup-6
chore(deps): bump pnpm/action-setup from 5 to 6
2026-04-14 11:46:31 +02:00
Daniel Radl
8d70f37c68
Merge pull request #1870 from frappe/dependabot/github_actions/actions/upload-pages-artifact-5
chore(deps): bump actions/upload-pages-artifact from 4 to 5
2026-04-14 10:42:40 +02:00
dependabot[bot]
db8868b25b
chore(deps): bump actions/upload-pages-artifact from 4 to 5
Bumps [actions/upload-pages-artifact](https://github.com/actions/upload-pages-artifact) from 4 to 5.
- [Release notes](https://github.com/actions/upload-pages-artifact/releases)
- [Commits](https://github.com/actions/upload-pages-artifact/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/upload-pages-artifact
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-14 08:23:11 +00:00
RocketQuack
4c6d4decca
Merge pull request #1868 from frappe/dependabot/github_actions/docker/bake-action-7.1.0
chore(deps): bump docker/bake-action from 7.0.0 to 7.1.0
2026-04-13 14:46:14 +02:00
dependabot[bot]
3e1e045f7a
chore(deps): bump pnpm/action-setup from 5 to 6
Bumps [pnpm/action-setup](https://github.com/pnpm/action-setup) from 5 to 6.
- [Release notes](https://github.com/pnpm/action-setup/releases)
- [Commits](https://github.com/pnpm/action-setup/compare/v5...v6)

---
updated-dependencies:
- dependency-name: pnpm/action-setup
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-13 08:39:38 +00:00
dependabot[bot]
526119247f
chore(deps): bump docker/bake-action from 7.0.0 to 7.1.0
Bumps [docker/bake-action](https://github.com/docker/bake-action) from 7.0.0 to 7.1.0.
- [Release notes](https://github.com/docker/bake-action/releases)
- [Commits](https://github.com/docker/bake-action/compare/v7.0.0...v7.1.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-13 08:39:30 +00:00
Daniel Radl
bb3faf8a25
Merge pull request #1867 from AMR-Mannesmann/docs/add-maintainers-file
docs: add MAINTAINERS.md to document project maintainers
2026-04-12 15:15:16 +02:00
Daniel Radl
adaf37dfa5 docs: add MAINTAINERS.md to document project maintainers 2026-04-12 15:12:42 +02:00
github-actions
f36bde7aca chore: Update example.env 2026-04-11 05:32:15 +00:00
Daniel Radl
9d566ecc3d
Merge pull request #1866 from AMR-Mannesmann/chore/vscode-watcher-exclude
chore(vscode): exclude build artifacts and deps from file watcher
2026-04-10 17:33:34 +02:00
Daniel Radl
91308ce43d chore(vscode): exclude build artifacts and deps from file watcher 2026-04-10 16:51:19 +02:00
RocketQuack
1aa7f670ad
Merge pull request #1864 from Rocket-Quack/fix/security-updates-2
fix(docs): override vulnerable vite dependency
2026-04-09 13:26:55 +02:00
RocketQuack
90d9d25eb3 fix(docs): override vulnerable vite dependency 2026-04-09 13:17:52 +02:00
github-actions
dfb6ee4f08 chore: Update example.env 2026-04-09 10:37:09 +00:00
github-actions
20169854f5 chore: Update example.env 2026-04-09 05:17:52 +00:00
RocketQuack
7382ba247c
Merge pull request #1862 from frappe/dependabot/pip/pytest-9.0.3
chore(deps): bump pytest from 9.0.2 to 9.0.3
2026-04-08 14:15:03 +02:00
dependabot[bot]
f521624b1b
chore(deps): bump pytest from 9.0.2 to 9.0.3
Bumps [pytest](https://github.com/pytest-dev/pytest) from 9.0.2 to 9.0.3.
- [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.2...9.0.3)

---
updated-dependencies:
- dependency-name: pytest
  dependency-version: 9.0.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-08 08:24:16 +00:00
github-actions
031d1964a1 chore: Update example.env 2026-04-07 18:26:31 +00:00
OmarElaraby26
8892908f5d docs: require Docker Engine v23+ instead of setting DOCKER_BUILDKIT=1
BuildKit has been the default builder since Docker Engine 23.0 (Feb 2023),
so prefixing the example build commands with DOCKER_BUILDKIT=1 is redundant
on any supported install. Replace the prefix with an explicit prerequisite
note so the requirement lives with the user's environment, not the example.

The build relies on BuildKit secret mounts (--secret) to keep apps.json
tokens out of image layers, which is why a real BuildKit-default engine
is mandatory rather than merely recommended.

Addresses review feedback on PR #1861.
2026-04-07 20:12:29 +02:00
OmarElaraby26
ae275df161 fix(security): replace APPS_JSON_BASE64 build-arg with BuildKit secret mount
APPS_JSON_BASE64 is stored in image layer metadata, permanently exposing
private repo tokens (GitHub PATs) to anyone with image pull access.

Replace --build-arg with --mount=type=secret so that apps.json is only
available during the RUN step and never committed to any layer.

Refs: https://docs.docker.com/reference/build-checks/secrets-used-in-arg-or-env/
2026-04-05 22:24:53 +02:00
RocketQuack
65d9510a2b
Merge pull request #1858 from sujaldev/patch-1
docs: fix dead link for environment variables reference.
2026-04-04 14:41:50 +02:00
Sujal Singh
259aa24c64 docs: fix dead link for environment variables reference 2026-04-04 06:44:45 +05:30
Daniel Radl
558c8b676c
Merge pull request #1856 from ews-pgasser/feat/add-pre-commit-postCreateCommand
Add pre-commit to devcontainer postCreateCommand
2026-04-03 20:08:32 +02:00
RocketQuack
44acc39d35
Merge pull request #1857 from BurningDog/fix/correct-link-to-env-vars-doc
fix: correct links to documentation
2026-04-03 12:40:32 +02:00
Roger Saner
563e895ad0
fix: update compose.yaml
Correct URL for Build Setup
2026-04-02 18:55:05 +02:00
Roger Saner
c7d61a5a6d
fix: update example.env
Correct link to env vars doc
2026-04-02 18:43:23 +02:00
ews-pgasser
c1236d9fcf feat: add pre-commit to devcontainer postCreateCommand 2026-04-02 16:01:41 +02:00
Daniel Radl
bc24e3190e
Merge pull request #1855 from ews-pgasser/fix/update-erpnext-to-version-16
Update Frappe and ERPNext branches to version 16
2026-04-02 13:49:56 +02:00
Daniel Radl
023af5214c
chore: add EOF newline 2026-04-02 13:48:10 +02:00
ews-pgasser
fc60479969 chore: update erpnext branch to version-16 2026-04-02 10:00:40 +02:00
ews-pgasser
6c05252b9f chore: update frappe branch to version-16 2026-04-02 10:00:33 +02:00
RocketQuack
1e78e56ffd
Merge pull request #1854 from Rocket-Quack/fix/docs-home-link
fix(docs): correct single compose setup home link
2026-04-01 19:58:07 +02:00
RocketQuack
56096de423
Merge pull request #1853 from Rocket-Quack/fix/security-updates
Fix/security updates
2026-04-01 19:45:08 +02:00
RocketQuack
8d83c2dd04 fix(docs): correct single compose setup home link 2026-04-01 19:44:12 +02:00
RocketQuack
3c076d0ecc fix(docs): override vulnerable minimatch and picomatch 2026-04-01 19:25:51 +02:00
RocketQuack
1b105dac84 fix(docs): override vulnerable brace-expansion dependency 2026-04-01 17:45:49 +02:00
Daniel Radl
65ab824d9e
Merge pull request #1852 from AMR-Mannesmann/fixnginx-redirects-leak-internal-port
fix: nginx redirects leak internal port
2026-03-31 16:45:20 +02:00
Daniel Radl
eccae398c5
fix(nginx): set absolute_redirect off 2026-03-31 16:40:41 +02:00
github-actions
40c741b751 chore: Update example.env 2026-03-30 18:30:39 +00:00
Daniel Radl
8ccccdbdd5
fix: nginx redirects leak internal port 2026-03-30 12:08:46 +02:00
RocketQuack
d1289a8604
Merge pull request #1849 from frappe/dependabot/github_actions/actions/deploy-pages-5
chore(deps): bump actions/deploy-pages from 4 to 5
2026-03-26 12:48:20 +01:00
dependabot[bot]
60a27952c9
chore(deps): bump actions/deploy-pages from 4 to 5
Bumps [actions/deploy-pages](https://github.com/actions/deploy-pages) from 4 to 5.
- [Release notes](https://github.com/actions/deploy-pages/releases)
- [Commits](https://github.com/actions/deploy-pages/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/deploy-pages
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-26 08:22:38 +00:00
RocketQuack
e5ce055690
Merge pull request #1848 from trustedcomputer/fix-snippits-permission-error
fix: copy security_headers.conf before chown
2026-03-25 10:06:23 +01:00
Trusted Computer
51e3fa8a46
fix: copy security_headers.conf before chown 2026-03-24 18:48:20 -07:00
RocketQuack
c1b9bf4633
Merge pull request #1844 from frappe/dependabot/github_actions/pnpm/action-setup-5
chore(deps): bump pnpm/action-setup from 4 to 5
2026-03-24 17:31:30 +01:00
github-actions
316c02bd88 chore: Update example.env 2026-03-23 17:02:54 +00:00
dependabot[bot]
20c040c25b
chore(deps): bump pnpm/action-setup from 4 to 5
Bumps [pnpm/action-setup](https://github.com/pnpm/action-setup) from 4 to 5.
- [Release notes](https://github.com/pnpm/action-setup/releases)
- [Commits](https://github.com/pnpm/action-setup/compare/v4...v5)

---
updated-dependencies:
- dependency-name: pnpm/action-setup
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-20 08:22:28 +00:00
RocketQuack
a1ee343f21
Merge pull request #1842 from frappe/dependabot/npm_and_yarn/docs/minimatch-10.2.4
chore(deps): bump minimatch from 10.1.1 to 10.2.4 in /docs
2026-03-19 13:41:09 +01:00
RocketQuack
08e97d1dd2 Merge branch 'dependabot/npm_and_yarn/docs/minimatch-10.2.4' of https://github.com/frappe/frappe_docker into dependabot/npm_and_yarn/docs/minimatch-10.2.4 2026-03-19 13:39:42 +01:00
RocketQuack
b0cd694f6e
Merge pull request #1841 from frappe/dependabot/npm_and_yarn/docs/isaacs/brace-expansion-5.0.1
chore(deps): bump @isaacs/brace-expansion from 5.0.0 to 5.0.1 in /docs
2026-03-19 12:27:46 +01:00
RocketQuack
2c7527c121 chore(pre-commit): exclude docs pnpm lockfile from prettier 2026-03-19 12:25:24 +01:00
dependabot[bot]
625480da64
chore(deps): bump minimatch from 10.1.1 to 10.2.4 in /docs
Bumps [minimatch](https://github.com/isaacs/minimatch) from 10.1.1 to 10.2.4.
- [Changelog](https://github.com/isaacs/minimatch/blob/main/changelog.md)
- [Commits](https://github.com/isaacs/minimatch/compare/v10.1.1...v10.2.4)

---
updated-dependencies:
- dependency-name: minimatch
  dependency-version: 10.2.4
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-19 11:02:18 +00:00
dependabot[bot]
b314cfd60c
chore(deps): bump @isaacs/brace-expansion from 5.0.0 to 5.0.1 in /docs
Bumps @isaacs/brace-expansion from 5.0.0 to 5.0.1.

---
updated-dependencies:
- dependency-name: "@isaacs/brace-expansion"
  dependency-version: 5.0.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-19 11:02:11 +00:00
RocketQuack
ca1eac5d7c
Merge pull request #1805 from adithyaappu/docs/vitepress-docs
Docs/vitepress docs
2026-03-19 12:01:10 +01:00
RocketQuack
ce1d7b301a fix(lint): resolve pre-commit formatting 2026-03-19 11:57:06 +01:00
RocketQuack
1b8ddeadfd chore(packages): pin vitepress-sidebar version 2026-03-19 11:56:06 +01:00
github-actions
c5af709a32 chore: Update example.env 2026-03-18 14:11:27 +00:00
RocketQuack
3ce80020b7
Merge pull request #1838 from RinZ27/fix-nginx-header-inheritance
Ensure security headers are preserved in /files location
2026-03-18 10:04:26 +01:00
github-actions
010278ea2e chore: Update example.env 2026-03-18 05:26:24 +00:00
RinZ27
730e31805b
style: fix linting issues with black 2026-03-18 10:14:54 +07:00
Rocket-Quack
3ffa8b720c test(nginx): add regression coverage for /files headers 2026-03-17 16:08:25 +01:00
Rocket-Quack
00c3475943 build(docker images): add nginx security headers snippet in production and custom images 2026-03-17 15:54:01 +01:00
Rocket-Quack
c40113923c refactor(nginx): include headers snippet via correct runtime path 2026-03-17 15:52:42 +01:00
Rin
57287e9cff Refactor: Move shared security headers into a snippet and include it in server and files location blocks. 2026-03-16 23:11:41 +07:00
RocketQuack
986a8f15f1
Merge pull request #1839 from frappe/dependabot/github_actions/webfactory/ssh-agent-0.10.0
chore(deps): bump webfactory/ssh-agent from 0.9.1 to 0.10.0
2026-03-16 16:05:48 +01:00
RocketQuack
63e163e6b0
Merge pull request #1837 from IB2357/fix/redis-network-isolation
fix(overrides): remove redis services from mariadb network in multi-bench setup
2026-03-16 15:55:11 +01:00
Daniel Radl
cb8b01658c
Merge pull request #1834 from nishanthabimanyu/fix/update-postgres-override
fix(overrides): update PostgreSQL to v15 and improve v16 compatibility
2026-03-16 11:23:35 +01:00
adithya
c1e545f3e5 refactor: Migrating Comprehensive Guide
The Comprehensive Getting Started guide is broken into smaller pieces
and added to respective sections.
2026-03-16 00:02:01 +05:30
adithya
655a1f4ec8 fix: More details in contributing 2026-03-16 00:01:28 +05:30
adithya
d433f52e9a ore: VitePress version bump 2026-03-16 00:01:11 +05:30
adithya
f4faecc1d6 feat: Added Contribution Guide
The README.md gives a brief overview about how to contribute to
documentation, and more detailed isntructions specific to VitePress is
added as a new page under References.
2026-03-15 18:47:35 +05:30
adithya
bb06d6bb09 fix: Added missing title metadata
New files didn't have YAML metadata, it is added now.
2026-03-15 18:47:35 +05:30
adithya
208aa7b7f4 fix: Header fix
Header in TLS / SSL setup was accidentally reverted to older one. Now
replaced with the new one.
2026-03-15 18:47:35 +05:30
adithya
477376edc4 refactor: Moved md files back to docs
Refactor can be considered later if needed.
2026-03-15 18:47:35 +05:30
adithya
64a008d188 fix: Changed branch to main
For creating pull request
2026-03-15 18:47:35 +05:30
adithya
98f83f9676 feat: added favicon 2026-03-15 18:47:35 +05:30
adithya
b624727012 fix: Sidebar config issue
Properly source the sidebar links
2026-03-15 18:47:35 +05:30
adithya
2901865e74 fix: Frappe Docker logo
moved public folder to docs.
2026-03-15 18:47:35 +05:30
adithya
cab78405cc refactor: Moving md files to src for clarity 2026-03-15 18:47:35 +05:30
adithya
c81d6c794b fix: Contain all docs and related in docs folder
The package.json and pnpm-lock.yaml are not part of the main repo but
specific to the docs. The workflow will now target this folder for
running.
2026-03-15 18:47:35 +05:30
adithya
a48633d33c feat: Deploy to GitHub pages
fix: pnpm Actions fix, corrected base

fix: Build error fixes

fix: Images not showing up in pages

fix: Moved logo to public path
2026-03-15 18:47:35 +05:30
adithya
e7f2c66e1d feat: VitePress for docs
VitePress is configured in the repo for handling docs
2026-03-15 18:47:35 +05:30
github-actions
7228b5005c chore: Update example.env 2026-03-15 08:49:55 +00:00
dependabot[bot]
ad9d95d377
chore(deps): bump webfactory/ssh-agent from 0.9.1 to 0.10.0
Bumps [webfactory/ssh-agent](https://github.com/webfactory/ssh-agent) from 0.9.1 to 0.10.0.
- [Release notes](https://github.com/webfactory/ssh-agent/releases)
- [Changelog](https://github.com/webfactory/ssh-agent/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webfactory/ssh-agent/compare/v0.9.1...v0.10.0)

---
updated-dependencies:
- dependency-name: webfactory/ssh-agent
  dependency-version: 0.10.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-12 08:23:02 +00:00
RinZ27
904df21d43
Fix Nginx header inheritance for /files location 2026-03-11 21:43:18 +07:00
github-actions
91a190e924 chore: Update example.env 2026-03-10 15:16:05 +00:00
IB2357
d6045cc9ba fix(overrides): remove redis services from mariadb network in multi-bench setup 2026-03-10 15:09:26 +03:00
github-actions
9a5f17f09b chore: Update example.env 2026-03-10 07:13:59 +00:00
nishanthabimanyu
5191d1965d style: fix prettier formatting for postgres migration guide 2026-03-10 03:50:02 +00:00
nishanthabimanyu
15648342dd docs(postgres): add migration guide and update to postgres 15.17 2026-03-09 16:17:43 +00:00
Daniel Radl
51b5962aa6
Merge pull request #1836 from frappe/dependabot/github_actions/docker/bake-action-7.0.0
chore(deps): bump docker/bake-action from 6.10.0 to 7.0.0
2026-03-06 14:01:46 +01:00
github-actions
09ce533de1 chore: Update example.env 2026-03-06 09:05:35 +00:00
dependabot[bot]
ee4aaee2ae
chore(deps): bump docker/bake-action from 6.10.0 to 7.0.0
Bumps [docker/bake-action](https://github.com/docker/bake-action) from 6.10.0 to 7.0.0.
- [Release notes](https://github.com/docker/bake-action/releases)
- [Commits](https://github.com/docker/bake-action/compare/v6.10.0...v7.0.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-06 08:22:24 +00:00
nishanthabimanyu
9c777a56e5 fix(overrides): clear db command for postgres to fix CI merge conflicts 2026-03-06 06:03:56 +00:00
nishanthabimanyu
47ac9e8422 fix(overrides): simplify postgres override and use consistent default password 2026-03-06 05:40:47 +00:00
nishanthabimanyu
bf1f08797d fix(overrides): use default DB_PASSWORD to fix CI failures 2026-03-06 05:30:02 +00:00
nishanthabimanyu
b4e0501c3d fix(overrides): update PostgreSQL to v15 and improve v16 compatibility 2026-03-06 05:11:19 +00:00
Daniel Radl
a6fba64246
Merge pull request #1827 from 0samashaikh/fix-nginx-forwarded-proto-webserver
fix(nginx): use X-Forwarded-Proto for backend requests behind reverse proxy
2026-03-05 20:49:15 +01:00
Daniel Radl
dcafa5a416
Merge pull request #1833 from frappe/dependabot/github_actions/docker/setup-buildx-action-4
chore(deps): bump docker/setup-buildx-action from 3 to 4
2026-03-05 20:41:13 +01:00
Daniel Radl
3a27f333a0
Merge pull request #1832 from frappe/dependabot/github_actions/docker/login-action-4
chore(deps): bump docker/login-action from 3 to 4
2026-03-05 20:40:53 +01:00
Daniel Radl
2b0ecab48d
Merge pull request #1830 from frappe/dependabot/github_actions/docker/setup-qemu-action-4
chore(deps): bump docker/setup-qemu-action from 3 to 4
2026-03-05 20:38:34 +01:00
github-actions
455ef1f725 chore: Update example.env 2026-03-05 12:26:28 +00:00
dependabot[bot]
6dd26933cc
chore(deps): bump docker/setup-buildx-action from 3 to 4
Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 3 to 4.
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](https://github.com/docker/setup-buildx-action/compare/v3...v4)

---
updated-dependencies:
- dependency-name: docker/setup-buildx-action
  dependency-version: '4'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-05 08:33:15 +00:00
dependabot[bot]
a122219d0e
chore(deps): bump docker/login-action from 3 to 4
Bumps [docker/login-action](https://github.com/docker/login-action) from 3 to 4.
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](https://github.com/docker/login-action/compare/v3...v4)

---
updated-dependencies:
- dependency-name: docker/login-action
  dependency-version: '4'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-05 08:33:07 +00:00
dependabot[bot]
9dd00afc3c
chore(deps): bump docker/setup-qemu-action from 3 to 4
Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 3 to 4.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](https://github.com/docker/setup-qemu-action/compare/v3...v4)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-version: '4'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-04 08:21:45 +00:00
github-actions
c315c72eaa chore: Update example.env 2026-03-03 18:24:24 +00:00
Osama Shaikh
8070e5822e
fix(nginx): use X-Forwarded-Proto for backend requests behind reverse proxy 2026-03-03 13:36:46 +05:30
Nishanth Abimanyu
6e8384827f
fix(bake): synchronize production build versions for v16 compliance (#1810) (#1823)
* fix(bake): synchronize stable python 3.13 and node 22 to resolve issues #1810 and #1792

* fix(bake): update to python 3.14.2 and node 24.13.0 for v16 compliance per maintainer feedback
2026-03-03 06:16:06 +05:30
Osama Shaikh
0ec329fe34
fix(nginx): forward correct proto/origin for socket.io behind proxy (#1817) 2026-03-03 06:13:45 +05:30
github-actions
85aaca4bd5 chore: Update example.env 2026-02-26 08:57:12 +00:00
github-actions
4dd24f1fd0 chore: Update example.env 2026-02-26 07:23:06 +00:00
github-actions
4cac43fda3 chore: Update example.env 2026-02-26 06:32:16 +00:00
github-actions
73edefc49d chore: Update example.env 2026-02-25 07:01:07 +00:00
Daniel Radl
ce4c38f5d7
Merge pull request #1815 from trustedcomputer/feat-add-chromium-pdf-generator-support
feat: add chromium pdf generator support
2026-02-19 18:22:33 +01:00
Trusted Computer
2c43d34646
feat: add chromium pdf generator support 2026-02-19 09:10:18 -08:00
Daniel Radl
f0a511879e
Merge pull request #1816 from Rocket-Quack/fix/acme-companion-container-id
fix: Add acme-companion nginx discovery label to nginx-proxy
2026-02-19 13:37:08 +01:00
github-actions
878fd85511 chore: Update example.env 2026-02-19 05:29:17 +00:00
RocketQuack
38d96e1d95 fix: Add label for acme discovery to nginx-proxy 2026-02-18 17:31:35 +01:00
github-actions
7325c316b8 chore: Update example.env 2026-02-17 14:39:17 +00:00
trustedcomputer
8b3def7162 feat: add chromium pdf generator support 2026-02-16 06:51:59 -08:00
Thomas
f5598cd5e2
fix: enable pnpm via corepack for apps that require it (e.g. drive) 2026-02-11 17:00:48 +01:00
github-actions
d56cd8851e chore: Update example.env 2026-02-11 06:29:21 +00:00
github-actions
1ad2bba116 chore: Update example.env 2026-02-11 05:23:38 +00:00
Daniel Radl
d38e79e4a3
Merge pull request #1807 from AMR-Mannesmann/Update-workflow-to-remove-v14-and-use-v16
Update workflow to remove v14 and use v16
2026-02-07 17:54:45 +01:00
Daniel Radl
ef90ecb5aa
Update workflow to remove v14 and use v16 2026-02-07 17:41:21 +01:00
Daniel Radl
7784c78fb7
Merge pull request #1802 from david-loe/patch-1
Update Python and Node.js versions in Containerfile
Drop frappe `v14` compatibility
2026-02-07 17:07:05 +01:00
david-loe
4de2ce5307
Update FRAPPE_BRANCH to version-16 2026-02-07 09:57:45 +01:00
david-loe
abd0554e65
changed _V14 to _PREV 2026-02-07 09:56:19 +01:00
RocketQuack
99d9a1dc38
feat: add nginx-proxy with acme companion as an alternative to traefik (#1800)
* refactor: move core nginx files into more recognizable folder structure

* chore: include HTTPS_PUBLISH_PORT in example .env

* feat: add nginx-proxy and acme-companion compose overrides

* docs: add NGINX_PROXY_HOSTS to example.env

* docs: add nginx-proxy overrides

* docs: split docs, variables for usage of traefik or nginx-proxy

* docs: update override notes for traefik proxy on separate stack

* docs: split TLS/SSL overview and add own caddy guide

* docs: add nginx-proxy + acme companion guide

* docs: add nginx-proxy and acme single-server setup guide
2026-02-06 09:56:28 +05:30
github-actions
3d46cb84e0 chore: Update example.env 2026-02-05 11:24:40 +00:00
github-actions
63b75a4431 chore: Update example.env 2026-02-03 17:45:28 +00:00
david-loe
4a29d63c52
Update Frappe branch version to 16 2026-01-31 09:08:17 +01:00
david-loe
fbf00c802d
Update Python, Node, and Frappe versions in Containerfile 2026-01-31 09:06:00 +01:00
david-loe
30efa35347
Update Python and Node.js versions in Containerfile 2026-01-30 16:48:12 +01:00
github-actions
23593bfadb chore: Update example.env 2026-01-29 13:24:18 +00:00
github-actions
9a429695a8 chore: Update example.env 2026-01-28 04:39:33 +00:00
100 changed files with 5051 additions and 486 deletions

View file

@ -49,6 +49,13 @@ def update_env(file_name: str, frappe_tag: str, erpnext_tag: str | None = None):
f.write(text)
def update_output(file_name: str, frappe_tag: str, erpnext_tag: str | None = None):
with open(file_name, "a", encoding="utf-8") as f:
f.write(f"frappe_version={frappe_tag}\n")
if erpnext_tag:
f.write(f"erpnext_version={erpnext_tag}\n")
def _print_resp(frappe_tag: str, erpnext_tag: str | None = None):
print(json.dumps({"frappe": frappe_tag, "erpnext": erpnext_tag}))
@ -70,6 +77,9 @@ def main(_args: list[str]) -> int:
file_name = os.getenv("GITHUB_ENV")
if file_name:
update_env(file_name, frappe_tag, erpnext_tag)
file_name = os.getenv("GITHUB_OUTPUT")
if file_name:
update_output(file_name, frappe_tag, erpnext_tag)
_print_resp(frappe_tag, erpnext_tag)
return 0

189
.github/workflows/app-build-image.yml vendored Normal file
View file

@ -0,0 +1,189 @@
name: App / Build Image
on:
workflow_call:
inputs:
app_name:
required: true
type: string
description: "App module and image name, for example 'crm'"
app_repo:
required: true
type: string
description: "Git URL or GitHub slug for the app repository"
app_ref:
required: true
type: string
description: "Git branch or tag to install for the app"
frappe_ref:
required: true
type: string
description: "Tag of the existing frappe/base and frappe/build images, for example version-16"
frappe_image_prefix:
required: false
type: string
default: frappe
description: "Image prefix for existing base and build images, for example 'frappe' or 'ghcr.io/frappe'"
image_name:
required: true
type: string
description: "Full image name, for example ghcr.io/frappe/crm"
image_tag:
required: true
type: string
description: "Image tag, for example develop or v16.0.0"
push:
required: true
type: boolean
registry:
required: false
type: string
default: docker.io
frappe_repo:
required: false
type: string
default: https://github.com/frappe/frappe
description: "Git URL for the Frappe framework repository"
builder_repository:
required: false
type: string
default: frappe/frappe_docker
description: "Repository that contains the Containerfile and helper scripts"
builder_ref:
required: false
type: string
default: main
description: "Ref to checkout from the builder repository"
platforms:
required: false
type: string
default: linux/amd64
description: "Docker platforms for the final build"
secrets:
REGISTRY_USERNAME:
required: false
REGISTRY_PASSWORD:
required: false
permissions:
contents: read
packages: write
concurrency:
group: app-image-${{ github.repository }}-${{ inputs.app_name }}-${{ inputs.app_ref }}
cancel-in-progress: true
jobs:
build:
runs-on: ubuntu-latest
timeout-minutes: 30
env:
BUILDER_DIR: builder
APPS_JSON_PATH: builder/.github/tmp/apps.json
CACHE_SCOPE: app-image-${{ inputs.app_name }}-${{ inputs.frappe_ref }}
TEST_IMAGE: local/${{ inputs.app_name }}:${{ github.run_id }}-${{ github.run_attempt }}
FINAL_IMAGE: ${{ inputs.image_name }}:${{ inputs.image_tag }}
steps:
- name: Checkout builder repository
uses: actions/checkout@v6
with:
repository: ${{ inputs.builder_repository }}
ref: ${{ inputs.builder_ref }}
path: ${{ env.BUILDER_DIR }}
- name: Setup QEMU
uses: docker/setup-qemu-action@v4
with:
image: tonistiigi/binfmt:latest
platforms: all
- name: Setup Buildx
uses: docker/setup-buildx-action@v4
- name: Create apps.json
env:
APP_REPO: ${{ inputs.app_repo }}
APP_REF: ${{ inputs.app_ref }}
APPS_JSON_PATH: ${{ env.APPS_JSON_PATH }}
run: |
mkdir -p "$(dirname "$APPS_JSON_PATH")"
python3 - <<'PY'
import json
import os
from pathlib import Path
repo = os.environ["APP_REPO"].strip()
ref = os.environ["APP_REF"].strip()
if repo.count("/") == 1 and not repo.startswith(("https://", "http://")):
repo = f"https://github.com/{repo}"
for prefix in ("refs/heads/", "refs/tags/"):
if ref.startswith(prefix):
ref = ref.removeprefix(prefix)
Path(os.environ["APPS_JSON_PATH"]).write_text(
json.dumps([{"url": repo, "branch": ref}], indent=2) + "\n",
encoding="utf-8",
)
PY
- name: Build smoke-test image
uses: docker/build-push-action@v7
with:
context: ${{ env.BUILDER_DIR }}
file: ${{ env.BUILDER_DIR }}/images/layered/Containerfile
build-args: |
FRAPPE_IMAGE_PREFIX=${{ inputs.frappe_image_prefix }}
FRAPPE_PATH=${{ inputs.frappe_repo }}
FRAPPE_BRANCH=${{ inputs.frappe_ref }}
cache-from: type=gha,scope=${{ env.CACHE_SCOPE }}
cache-to: type=gha,mode=max,scope=${{ env.CACHE_SCOPE }}
load: true
platforms: linux/amd64
secrets: |
id=apps_json,src=${{ env.APPS_JSON_PATH }}
tags: ${{ env.TEST_IMAGE }}
- name: Smoke test image contents
env:
APP_NAME: ${{ inputs.app_name }}
TEST_IMAGE: ${{ env.TEST_IMAGE }}
run: |
docker run --rm --entrypoint bash "$TEST_IMAGE" -lc \
"test -d /home/frappe/frappe-bench/apps/frappe && test -d /home/frappe/frappe-bench/apps/${APP_NAME}"
- name: Login to GHCR
if: ${{ inputs.push && inputs.registry == 'ghcr.io' }}
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ github.token }}
- name: Login to target registry
if: ${{ inputs.push && inputs.registry != 'ghcr.io' }}
uses: docker/login-action@v4
with:
registry: ${{ inputs.registry }}
username: ${{ secrets.REGISTRY_USERNAME }}
password: ${{ secrets.REGISTRY_PASSWORD }}
- name: Push multi-arch image
if: ${{ inputs.push }}
uses: docker/build-push-action@v7
with:
context: ${{ env.BUILDER_DIR }}
file: ${{ env.BUILDER_DIR }}/images/layered/Containerfile
build-args: |
FRAPPE_IMAGE_PREFIX=${{ inputs.frappe_image_prefix }}
FRAPPE_PATH=${{ inputs.frappe_repo }}
FRAPPE_BRANCH=${{ inputs.frappe_ref }}
cache-from: type=gha,scope=${{ env.CACHE_SCOPE }}
cache-to: type=gha,mode=max,scope=${{ env.CACHE_SCOPE }}
platforms: ${{ inputs.platforms }}
push: true
secrets: |
id=apps_json,src=${{ env.APPS_JSON_PATH }}
tags: ${{ env.FINAL_IMAGE }}

View file

@ -1,33 +1,12 @@
name: Develop build
name: Legacy / Build Develop
on:
pull_request:
branches:
- main
paths:
- images/production/**
- overrides/**
- tests/**
- compose.yaml
- docker-bake.hcl
- example.env
- .github/workflows/build_develop.yml
schedule:
# Every day at 12:00 pm
- cron: 0 0 * * *
workflow_dispatch:
jobs:
build:
uses: ./.github/workflows/docker-build-push.yml
with:
repo: erpnext
version: develop
push: ${{ github.repository == 'frappe/frappe_docker' && github.event_name != 'pull_request' }}
python_version: 3.14.2
node_version: 24.12.0
secrets:
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
delegate:
uses: ./.github/workflows/core-build-develop.yml
permissions:
contents: read
packages: write
secrets: inherit

View file

@ -1,128 +1,12 @@
name: Stable build
name: Legacy / Build Stable
on:
pull_request:
branches:
- main
paths:
- images/production/**
- overrides/**
- tests/**
- compose.yaml
- docker-bake.hcl
- example.env
- .github/workflows/build_stable.yml
push:
branches:
- main
paths:
- images/production/**
- overrides/**
- tests/**
- compose.yaml
- docker-bake.hcl
- example.env
# Triggered from frappe/frappe and frappe/erpnext on releases
repository_dispatch:
workflow_dispatch:
jobs:
v14:
uses: ./.github/workflows/docker-build-push.yml
with:
repo: erpnext
version: "14"
push: ${{ github.repository == 'frappe/frappe_docker' && github.event_name != 'pull_request' }}
python_version: 3.10.13
node_version: 16.20.2
secrets:
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
v15:
uses: ./.github/workflows/docker-build-push.yml
with:
repo: erpnext
version: "15"
push: ${{ github.repository == 'frappe/frappe_docker' && github.event_name != 'pull_request' }}
python_version: 3.11.6
node_version: 20.19.2
secrets:
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
v16:
uses: ./.github/workflows/docker-build-push.yml
with:
repo: erpnext
version: "16"
push: ${{ github.repository == 'frappe/frappe_docker' && github.event_name != 'pull_request' }}
python_version: 3.14.2
node_version: 24.12.0
secrets:
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
update_versions:
name: Update example.env and pwd.yml
runs-on: ubuntu-latest
if: ${{ github.repository == 'frappe/frappe_docker' && github.event_name != 'pull_request' }}
needs: v15
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup Python
uses: actions/setup-python@v6
with:
python-version: "3.10"
- name: Get latest versions
run: python3 ./.github/scripts/get_latest_tags.py --repo erpnext --version 15
- name: Update
run: |
python3 ./.github/scripts/update_example_env.py
python3 ./.github/scripts/update_pwd.py
- name: Push
run: |
git config --global user.name github-actions
git config --global user.email github-actions@github.com
git add example.env pwd.yml
if [ -z "$(git status --porcelain)" ]; then
echo "versions did not change, exiting."
exit 0
else
echo "version changed, pushing changes..."
git commit -m "chore: Update example.env"
git pull --rebase
git push origin main
fi
release_helm:
name: Release Helm
runs-on: ubuntu-latest
if: ${{ github.repository == 'frappe/frappe_docker' && github.event_name != 'pull_request' }}
needs: v15
steps:
- name: Setup deploy key
uses: webfactory/ssh-agent@v0.9.1
with:
ssh-private-key: ${{ secrets.HELM_DEPLOY_KEY }}
- name: Setup Git Credentials
run: |
git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
git config --global user.name "github-actions[bot]"
- name: Release
run: |
git clone git@github.com:frappe/helm.git && cd helm
pip install -r release_wizard/requirements.txt
./release_wizard/wizard 16 patch --remote origin --ci
delegate:
uses: ./.github/workflows/core-build-stable.yml
permissions:
contents: write
packages: write
secrets: inherit

View file

@ -1,4 +1,4 @@
name: Bench
name: Core / Build Bench
on:
pull_request:
@ -7,7 +7,7 @@ on:
paths:
- images/bench/**
- docker-bake.hcl
- .github/workflows/build_bench.yml
- .github/workflows/core-build-bench.yml
schedule:
# Every day at 12:00 pm
@ -23,13 +23,13 @@ jobs:
uses: actions/checkout@v6
- name: Setup QEMU
uses: docker/setup-qemu-action@v3
uses: docker/setup-qemu-action@v4
with:
image: tonistiigi/binfmt:latest
platforms: all
- name: Setup Buildx
uses: docker/setup-buildx-action@v3
uses: docker/setup-buildx-action@v4
- name: Set Environment Variables
run: cat example.env | grep -o '^[^#]*' >> "$GITHUB_ENV"
@ -38,21 +38,21 @@ 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@v6.10.0
uses: docker/bake-action@v7.2.0
with:
source: .
targets: bench-test
- name: Login
if: ${{ github.repository == 'frappe/frappe_docker' && github.event_name != 'pull_request' }}
uses: docker/login-action@v3
uses: docker/login-action@v4
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Push
if: ${{ github.repository == 'frappe/frappe_docker' && github.event_name != 'pull_request' }}
uses: docker/bake-action@v6.10.0
uses: docker/bake-action@v7.2.0
with:
targets: bench
push: true

View file

@ -0,0 +1,51 @@
name: Core / Build Develop
permissions:
contents: read
packages: write
on:
workflow_call:
pull_request:
branches:
- main
paths:
- images/production/**
- overrides/**
- tests/**
- compose.yaml
- docker-bake.hcl
- example.env
- .github/workflows/core-build-develop.yml
- .github/workflows/core-build-test-images.yml
- .github/workflows/core-publish-images.yml
schedule:
# Every day at 12:00 pm
- cron: 0 0 * * *
workflow_dispatch:
jobs:
test:
uses: ./.github/workflows/core-build-test-images.yml
with:
repo: erpnext
version: develop
python_version: 3.14.2
node_version: 24.12.0
publish:
if: ${{ github.repository == 'frappe/frappe_docker' && github.event_name != 'pull_request' }}
needs: test
uses: ./.github/workflows/core-publish-images.yml
with:
repo: erpnext
frappe_version: ${{ needs.test.outputs.frappe_version }}
erpnext_version: ${{ needs.test.outputs.erpnext_version }}
python_version: 3.14.2
node_version: 24.12.0
secrets:
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}

148
.github/workflows/core-build-stable.yml vendored Normal file
View file

@ -0,0 +1,148 @@
name: Core / Build Stable
permissions:
contents: read
packages: write
on:
workflow_call:
pull_request:
branches:
- main
paths:
- images/production/**
- overrides/**
- tests/**
- compose.yaml
- docker-bake.hcl
- example.env
- .github/workflows/core-build-stable.yml
- .github/workflows/core-build-test-images.yml
- .github/workflows/core-publish-images.yml
push:
branches:
- main
paths:
- images/production/**
- overrides/**
- tests/**
- compose.yaml
- docker-bake.hcl
- example.env
- .github/workflows/core-build-stable.yml
- .github/workflows/core-build-test-images.yml
- .github/workflows/core-publish-images.yml
# Triggered from frappe/frappe and frappe/erpnext on releases
repository_dispatch:
workflow_dispatch:
jobs:
v15_test:
uses: ./.github/workflows/core-build-test-images.yml
with:
repo: erpnext
version: "15"
python_version: 3.11.6
node_version: 20.19.2
v15_publish:
if: ${{ github.repository == 'frappe/frappe_docker' && github.event_name != 'pull_request' }}
needs: v15_test
uses: ./.github/workflows/core-publish-images.yml
with:
repo: erpnext
frappe_version: ${{ needs.v15_test.outputs.frappe_version }}
erpnext_version: ${{ needs.v15_test.outputs.erpnext_version }}
python_version: 3.11.6
node_version: 20.19.2
secrets:
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
v16_test:
uses: ./.github/workflows/core-build-test-images.yml
with:
repo: erpnext
version: "16"
python_version: 3.14.2
node_version: 24.12.0
v16_publish:
if: ${{ github.repository == 'frappe/frappe_docker' && github.event_name != 'pull_request' }}
needs: v16_test
uses: ./.github/workflows/core-publish-images.yml
with:
repo: erpnext
frappe_version: ${{ needs.v16_test.outputs.frappe_version }}
erpnext_version: ${{ needs.v16_test.outputs.erpnext_version }}
python_version: 3.14.2
node_version: 24.12.0
secrets:
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
update_versions:
name: Update example.env and pwd.yml
runs-on: ubuntu-latest
if: ${{ github.repository == 'frappe/frappe_docker' && github.event_name != 'pull_request' }}
needs: v16_publish
permissions:
contents: write
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup Python
uses: actions/setup-python@v6
with:
python-version: "3.14.2"
- name: Get latest versions
run: python3 ./.github/scripts/get_latest_tags.py --repo erpnext --version 16
- name: Update
run: |
python3 ./.github/scripts/update_example_env.py
python3 ./.github/scripts/update_pwd.py
- name: Push
run: |
git config --global user.name github-actions
git config --global user.email github-actions@github.com
git add example.env pwd.yml
if [ -z "$(git status --porcelain)" ]; then
echo "versions did not change, exiting."
exit 0
else
echo "version changed, pushing changes..."
git commit -m "chore: Update example.env"
git pull --rebase
git push origin main
fi
release_helm:
name: Release Helm
runs-on: ubuntu-latest
if: ${{ github.repository == 'frappe/frappe_docker' && github.event_name != 'pull_request' }}
needs: v16_publish
steps:
- name: Setup deploy key
uses: webfactory/ssh-agent@v0.10.0
with:
ssh-private-key: ${{ secrets.HELM_DEPLOY_KEY }}
- name: Setup Git Credentials
run: |
git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
git config --global user.name "github-actions[bot]"
- name: Release
run: |
git clone git@github.com:frappe/helm.git && cd helm
pip install -r release_wizard/requirements.txt
./release_wizard/wizard 16 patch --remote origin --ci

View file

@ -1,4 +1,4 @@
name: Build
name: Core / Build and Test Images
on:
workflow_call:
@ -11,9 +11,6 @@ on:
required: true
type: string
description: "Major version, git tags should match 'v{version}.*'; or 'develop'"
push:
required: true
type: boolean
python_version:
required: true
type: string
@ -22,16 +19,36 @@ on:
required: true
type: string
description: NodeJS Version
secrets:
DOCKERHUB_USERNAME:
required: true
DOCKERHUB_TOKEN:
required: true
outputs:
frappe_version:
description: "Resolved frappe image tag"
value: ${{ jobs.resolve.outputs.frappe_version }}
erpnext_version:
description: "Resolved erpnext image tag"
value: ${{ jobs.resolve.outputs.erpnext_version }}
permissions:
contents: read
jobs:
resolve:
name: Resolve Versions
runs-on: ubuntu-latest
outputs:
frappe_version: ${{ steps.resolve.outputs.frappe_version }}
erpnext_version: ${{ steps.resolve.outputs.erpnext_version }}
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Resolve image versions
id: resolve
run: python3 ./.github/scripts/get_latest_tags.py --repo ${{ inputs.repo }} --version ${{ inputs.version }}
build:
name: Build
runs-on: ubuntu-latest
needs: resolve
services:
registry:
image: docker.io/registry:2
@ -46,19 +63,23 @@ jobs:
uses: actions/checkout@v6
- name: Setup QEMU
uses: docker/setup-qemu-action@v3
uses: docker/setup-qemu-action@v4
with:
image: tonistiigi/binfmt:latest
platforms: all
- name: Setup Buildx
uses: docker/setup-buildx-action@v3
uses: docker/setup-buildx-action@v4
with:
driver-opts: network=host
platforms: linux/${{ matrix.arch }}
- name: Get latest versions
run: python3 ./.github/scripts/get_latest_tags.py --repo ${{ inputs.repo }} --version ${{ inputs.version }}
- name: Set resolved versions
run: |
echo "FRAPPE_VERSION=${{ needs.resolve.outputs.frappe_version }}" >> "$GITHUB_ENV"
if [ -n "${{ needs.resolve.outputs.erpnext_version }}" ]; then
echo "ERPNEXT_VERSION=${{ needs.resolve.outputs.erpnext_version }}" >> "$GITHUB_ENV"
fi
- name: Set build args
run: |
@ -66,7 +87,7 @@ jobs:
echo "NODE_VERSION=${{ inputs.node_version }}" >> "$GITHUB_ENV"
- name: Build
uses: docker/bake-action@v6.10.0
uses: docker/bake-action@v7.2.0
with:
source: .
push: true
@ -85,17 +106,3 @@ jobs:
- name: Test
run: venv/bin/pytest --color=yes
- name: Login
if: ${{ inputs.push }}
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Push
if: ${{ inputs.push }}
uses: docker/bake-action@v6.10.0
with:
push: true
set: "*.platform=linux/amd64,linux/arm64"

View file

@ -0,0 +1,92 @@
name: Core / Publish Images
on:
workflow_call:
inputs:
repo:
required: true
type: string
description: "'erpnext' or 'frappe'"
frappe_version:
required: true
type: string
description: "Resolved frappe image tag"
erpnext_version:
required: false
type: string
description: "Resolved erpnext image tag"
python_version:
required: true
type: string
description: Python Version
node_version:
required: true
type: string
description: NodeJS Version
secrets:
DOCKERHUB_USERNAME:
required: true
DOCKERHUB_TOKEN:
required: true
permissions:
contents: read
packages: write
jobs:
publish:
name: Publish
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup QEMU
uses: docker/setup-qemu-action@v4
with:
image: tonistiigi/binfmt:latest
platforms: all
- name: Setup Buildx
uses: docker/setup-buildx-action@v4
- name: Set resolved versions
run: |
echo "FRAPPE_VERSION=${{ inputs.frappe_version }}" >> "$GITHUB_ENV"
if [ -n "${{ inputs.erpnext_version }}" ]; then
echo "ERPNEXT_VERSION=${{ inputs.erpnext_version }}" >> "$GITHUB_ENV"
fi
- name: Set build args
run: |
echo "PYTHON_VERSION=${{ inputs.python_version }}" >> "$GITHUB_ENV"
echo "NODE_VERSION=${{ inputs.node_version }}" >> "$GITHUB_ENV"
- name: Login to Docker Hub
uses: docker/login-action@v4
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Push Docker Hub images
uses: docker/bake-action@v7.2.0
with:
push: true
set: "*.platform=linux/amd64,linux/arm64"
- name: Login to GHCR
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Push GHCR base images
uses: docker/bake-action@v7.2.0
with:
targets: base-images
push: true
set: "*.platform=linux/amd64,linux/arm64"
env:
REGISTRY_USER: ghcr.io/${{ github.repository_owner }}

66
.github/workflows/docs-publish-site.yml vendored Normal file
View file

@ -0,0 +1,66 @@
name: Docs / Publish Site
on:
push:
branches: [main]
paths:
- "docs/**"
- ".github/workflows/docs-publish-site.yml"
workflow_dispatch:
permissions:
contents: read
pages: write
id-token: write
defaults:
run:
working-directory: ./docs
concurrency:
group: pages
cancel-in-progress: true
jobs:
build-and-deploy:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [24]
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v6
with:
node-version: ${{ matrix.node-version }}
- name: Enable Corepack
run: corepack enable
- name: Activate pnpm
run: corepack prepare pnpm@10.28.2 --activate
- name: Show tool versions
run: |
node --version
corepack --version
pnpm --version
which pnpm
- name: Install dependencies
run: pnpm i --frozen-lockfile
- name: Build Docs site
run: pnpm docs:build
- name: Upload artifact
uses: actions/upload-pages-artifact@v5
with:
path: docs/.vitepress/dist
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v5

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

4
.gitignore vendored
View file

@ -28,3 +28,7 @@ venv
# NodeJS
node_modules
# VitePress
**/.vitepress/dist
**/.vitepress/cache

View file

@ -27,6 +27,7 @@ repos:
rev: v4.0.0-alpha.8
hooks:
- id: prettier
exclude: ^docs/pnpm-lock\.yaml$
additional_dependencies:
- prettier@3.5.2
@ -34,6 +35,7 @@ repos:
rev: v2.4.1
hooks:
- id: codespell
exclude: ^docs/pnpm-lock\.yaml$
args:
- -L
- "ro"

View file

@ -100,9 +100,35 @@ Run pytest:
pytest
```
## Detailed Guidelines
A detailed form management guidelines are available in the [Fork Management](./docs/08-reference/03-fork-management.md)
# Documentation
Place relevant markdown files in the `docs` directory and index them in README.md located at the root of repo.
Documentation is written as markdown files, and placed inside the `docs/` directory. There are multiple sub directories under `docs/`, and be sure to place the `.md` file in the relevant sub directory if you are adding a new page.
If you want to include any image in the markdown file, place them in the `docs/images/` folder, and add a relative link in the `.md` file. For example if there is a `diagram.png` in the `docs/images/` directory, which has to be shown in a markdown file called `docs/01-getting-started/01-choosing-a-deployment-method.md` the image has to be referenced as,
```
![A diagram](../images/diagram.png)
```
Frappe Docker also have a static site version of the documentation, which is made using the same `.md` files in the `docs/` directory. Build pipeline uses [VitePress](https://vitepress.dev/) as the Static Site builder, which is a JavaScript (TypeScript) static site builder. Note that to contribute to the documentation JavaScript or VitePress knowledge is not needed. Updating the `.md` file is enough.
The only additional content needed that is specific to VitePress, is a ['frontmatter'](https://vitepress.dev/guide/frontmatter#frontmatter). Frontmatter is like the `metadata` or `config` of that specific `.md` file, added at the beginning of the file and enclosed in `---`. For example, the frontmatter can include a friendly title, author, date of publishing, etc. A more detailed overview on what is frontmatter can be found in this [blog](https://www.seancdavis.com/posts/wtf-is-frontmatter/).
In this project only one field is used in the frontmatter. The `title` field. This is used to specify the title of the page shown in the sidebar, which is either same or a simpler and smaller version of the first heading `#` of the page. To add the required frontmatter just add a block at the beginning of the `.md` file as shown below
```yaml
---
title: <short-title-for-sidebar>
---
```
In case of any doubt, just refer to any of the existing `.md` file. Also checkout the Markdown section in the VitePress documentation to see some additional features supported by VitePress: [Markdown Extensions](https://vitepress.dev/guide/markdown). Be careful not to break compatibility with what is supported by GitHub markdown. It is recommended to keep the documentation simple.
If you want details on how to configure or update VitePress specific settings and and functionalities, refer this page: [Configuring VitePress](https://github.com/frappe/frappe_docker/blob/main/docs/08-reference/02-configuring-vitepress.md)
# Frappe and ERPNext updates
@ -117,4 +143,4 @@ In case of new release of Debian. e.g. bullseye to bookworm. Change following fi
Change following files on release of ERPNext
- `.github/workflows/build_stable.yml`: Add the new release step under `jobs` and remove the unmaintained one. e.g. In case v12, v13 available, v14 will be added and v12 will be removed on release of v14. Also change the `needs:` for later steps to `v14` from `v13`.
- `.github/workflows/core-build-stable.yml`: Add the new release step under `jobs` and remove the unmaintained one. e.g. In case v12, v13 available, v14 will be added and v12 will be removed on release of v14. Also change the `needs:` for later steps to `v14` from `v13`.

25
MAINTAINERS.md Normal file
View file

@ -0,0 +1,25 @@
# Maintainers
This project is actively maintained by the following people.
Maintainers are responsible for:
- Reviewing and merging pull requests
- Managing releases
- Triaging and responding to issues
- Ensuring the overall health and direction of the project
## Current Maintainers
- [@revant](https://github.com/revant)
- [@DanielRadlAMR](https://github.com/DanielRadlAMR)
- [@Rocket-Quack](https://github.com/Rocket-Quack)
## Emeritus Maintainers
_(none)_
## Becoming a Maintainer
Contributors who consistently help review pull requests, participate in issue triage,
and contribute to releases may be invited to become maintainers.

View file

@ -1,17 +1,37 @@
# Frappe Docker
[![Build Stable](https://github.com/frappe/frappe_docker/actions/workflows/build_stable.yml/badge.svg)](https://github.com/frappe/frappe_docker/actions/workflows/build_stable.yml)
[![Build Develop](https://github.com/frappe/frappe_docker/actions/workflows/build_develop.yml/badge.svg)](https://github.com/frappe/frappe_docker/actions/workflows/build_develop.yml)
Docker images and orchestration for Frappe applications.
<div align="center">
<img src="docs/public/frappe-docker.png" alt="Frappe Docker" width="80" />
<h1>Frappe Docker</h1>
<p>Docker images and orchestration for Frappe applications.</p>
<p>
<a href="https://github.com/frappe/frappe_docker/actions/workflows/core-build-stable.yml">
<img src="https://img.shields.io/github/actions/workflow/status/frappe/frappe_docker/core-build-stable.yml?branch=main&label=Build%20Stable" alt="Build Stable" />
</a>
<a href="https://github.com/frappe/frappe_docker/actions/workflows/core-build-develop.yml">
<img src="https://img.shields.io/github/actions/workflow/status/frappe/frappe_docker/core-build-develop.yml?branch=main&label=Build%20Develop" alt="Build Develop" />
</a>
<a href="https://frappe.github.io/frappe_docker/">
<img src="https://img.shields.io/badge/Docs-Open%20Site-0A7EA4" alt="Docs" />
</a>
</p>
</div>
## What is this?
This repository handles the containerization of the Frappe stack, including the application server, database, Redis, and supporting services. It provides quick disposable demo setups, a development environment, production-ready Docker images and compose configurations for deploying Frappe applications including ERPNext.
This repository is the official container setup for Frappe applications.
It provides Docker images, Compose configurations, and documentation for running Frappe applications, including ERPNext, CRM, Helpdesk, and other Frappe apps, in containers.
Use it if you want to:
- run ERPNext, CRM, Helpdesk, or other Frappe apps with Docker
- start from a quick demo setup
- use production-ready Docker images and Compose setups
- build custom app images
- deploy and operate Frappe in production
## Repository Structure
```
```bash
frappe_docker/
├── docs/ # Complete documentation
├── overrides/ # Docker Compose configurations for different scenarios
@ -34,11 +54,18 @@ frappe_docker/
## Documentation
**The official documentation for `frappe_docker` is maintained in the `docs/` folder in this repository.**
The full `frappe_docker` documentation is available in [`docs/`](docs/) and published at [frappe.github.io/frappe_docker](https://frappe.github.io/frappe_docker/).
**New to Frappe Docker?** Read the [Getting Started Guide](docs/getting-started.md) for a comprehensive overview of repository structure, development workflow, custom apps, Docker concepts, and quick start examples.
### Recommended entry points:
If you are already familiar with Frappe, you can jump right into the [different deployment methods](docs/01-getting-started/01-choosing-a-deployment-method.md) and select the one best suited to your use case.
- **New here:** [Getting Started Guide](docs/getting-started.md)
- **Choosing a setup:** [Deployment methods](docs/01-getting-started/01-choosing-a-deployment-method.md)
- **ARM64 notes:** [ARM64](docs/01-getting-started/03-arm64.md)
- **Container setup overview:** [Container Setup Overview](docs/02-setup/01-overview.md)
- **Running in production:** [Production docs](docs/03-production/)
- **Operating a deployment:** [Operations docs](docs/04-operations/)
- **Development workflows:** [Development](docs/05-development/01-development.md)
- **FAQ:** [Frequently Asked Questions](https://github.com/frappe/frappe_docker/wiki/Frequently-Asked-Questions)
## Prerequisites
@ -50,17 +77,13 @@ If you are already familiar with Frappe, you can jump right into the [different
## Demo setup
The fastest way to try Frappe is to play in an already set up sandbox, in your browser, click the button below:
<a href="https://labs.play-with-docker.com/?stack=https://raw.githubusercontent.com/frappe/frappe_docker/main/pwd.yml">
<img src="https://raw.githubusercontent.com/play-with-docker/stacks/master/assets/images/button.png" alt="Try in PWD"/>
</a>
The fastest way to try Frappe locally is with the single-file demo setup in `pwd.yml`.
### Try on your environment
> **⚠️ Disposable demo only**
>
> **This setup is intended for quick evaluation. Expect to throw the environment away.** You will not be able to install custom apps to this setup. For production deployments, custom configurations, and detailed explanations, see the full documentation.
> **This setup is intended for short-lived evaluation only.** You will not be able to install custom apps to this setup. For production deployments, custom configurations, and detailed explanations, see the full documentation.
First clone the repo:
@ -77,22 +100,6 @@ docker compose -f pwd.yml up -d
Wait for a couple of minutes for ERPNext site to be created or check `create-site` container logs before opening browser on port `8080`. (username: `Administrator`, password: `admin`)
## Documentation Links
### [Getting Started Guide](docs/getting-started.md)
### [Frequently Asked Questions](https://github.com/frappe/frappe_docker/wiki/Frequently-Asked-Questions)
### [Getting Started](#getting-started)
### [Deployment Methods](docs/01-getting-started/01-choosing-a-deployment-method.md)
### [ARM64](docs/01-getting-started/03-arm64.md)
### [Container Setup Overview](docs/02-setup/01-overview.md)
### [Development](docs/05-development/01-development.md)
## Contributing
Contributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.

View file

@ -1,6 +1,6 @@
x-customizable-image: &customizable_image
# By default the image used only contains the `frappe` and `erpnext` apps.
# See https://github.com/frappe/frappe_docker/blob/main/docs/container-setup/02-build-setup.md#define-custom-apps
# See https://github.com/frappe/frappe_docker/blob/main/docs/02-setup/02-build-setup.md#define-custom-apps
# about using custom images.
image: ${CUSTOM_IMAGE:-frappe/erpnext}:${CUSTOM_TAG:-$ERPNEXT_VERSION}
pull_policy: ${PULL_POLICY:-always}
@ -33,6 +33,7 @@ services:
bench set-config -g redis_queue "redis://$$REDIS_QUEUE";
bench set-config -g redis_socketio "redis://$$REDIS_QUEUE";
bench set-config -gp socketio_port $$SOCKETIO_PORT;
bench set-config -g chromium_path /usr/bin/chromium-headless-shell;
environment:
DB_HOST: ${DB_HOST:-}
DB_PORT: ${DB_PORT:-}
@ -45,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": {
@ -44,6 +46,7 @@
"service": "frappe",
"workspaceFolder": "/workspace/development",
"shutdownAction": "stopCompose",
"postCreateCommand": "uv tool install pre-commit",
"mounts": [
"source=${localEnv:HOME}${localEnv:USERPROFILE}/.ssh,target=/home/frappe/.ssh,type=bind,consistency=cached"
]

View file

@ -5,7 +5,6 @@ services:
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_unicode_ci
- --skip-character-set-client-handshake
- --skip-innodb-read-only-compressed # Temporary fix for MariaDB 10.6
environment:
MYSQL_ROOT_PASSWORD: 123
MARIADB_AUTO_UPGRADE: 1

View file

@ -1,6 +1,6 @@
[
{
"url": "https://github.com/frappe/erpnext.git",
"branch": "version-15"
"branch": "version-16"
}
]

View file

@ -72,8 +72,8 @@ def get_args_parser():
"--frappe-branch",
action="store",
type=str,
help="frappe repo to use, default: version-15", # noqa: E501
default="version-15",
help="frappe repo to use, default: version-16", # noqa: E501
default="version-16",
)
parser.add_argument(
"-p",

View file

@ -1,3 +1,20 @@
{
"python.defaultInterpreterPath": "${workspaceFolder}/frappe-bench/env/bin/python"
"python.defaultInterpreterPath": "${workspaceFolder}/frappe-bench/env/bin/python",
"files.watcherExclude": {
// --- Node modules ---
"**/node_modules/**": true,
// --- Frappe bench core dirs ---
"**/env/**": true,
"**/config/**": true,
// --- Build artifacts ---
"**/__pycache__/**": true,
"**/*.pyc": true
},
"files.exclude": {
"**/__pycache__": true,
"**/*.pyc": true,
"**/.git": false
}
}

View file

@ -6,10 +6,10 @@ variable "REGISTRY_USER" {
}
variable PYTHON_VERSION {
default = "3.11.6"
default = "3.14.2"
}
variable NODE_VERSION {
default = "20.19.2"
default = "24.13.0"
}
variable "FRAPPE_VERSION" {
@ -62,6 +62,10 @@ group "default" {
targets = ["erpnext", "base", "build"]
}
group "base-images" {
targets = ["base", "build"]
}
function "tag" {
params = [repo, version]
result = [

View file

@ -0,0 +1,29 @@
import { defineConfig, UserConfig } from "vitepress";
import { withSidebar } from "vitepress-sidebar";
// https://vitepress.dev/reference/site-config
const vitePressOptions: UserConfig = {
title: "Frappe Docker Docs",
description: "Frappe in a Container",
base: "/frappe_docker/",
head: [["link", { rel: "icon", href: "/frappe_docker/favicon.png" }]],
themeConfig: {
logo: "/frappe-docker.png",
// https://vitepress.dev/reference/default-theme-config
nav: [{ text: "Home", link: "/" }],
socialLinks: [
{ icon: "github", link: "https://github.com/frappe/frappe_docker/" },
],
},
};
const vitePressSidebarOptions = {
documentRootPath: ".",
useTitleFromFrontmatter: true,
useFolderTitleFromIndexFile: true,
};
export default defineConfig(
withSidebar(vitePressOptions, vitePressSidebarOptions),
);

View file

@ -0,0 +1,92 @@
---
title: Introduction
---
# Introduction to Frappe Docker
This is the documentation for the Frappe Docker repository, which contains all the information on how to develop, deploy and share Frappe app, using Docker containers.
## Repository Architecture
Frappe Docker provides a comprehensive containerized environment for developing and deploying Frappe/ERPNext applications. It uses a **multi-service architecture** that handles everything from web serving to background job processing.
### Core Services
The base compose file includes these essential services:
- **configurator** - Initialization service that configures database and Redis connections; runs on startup and exits
- **backend** - Werkzeug development server for dynamic content processing
- **frontend** - Nginx reverse proxy that serves static assets and routes requests
- **websocket** - Node.js server running Socket.IO for real-time communications
- **queue-short/long** - Python workers using RQ (Redis Queue) for asynchronous background job processing
- **scheduler** - Python service that runs scheduled tasks using the schedule library
Additional services are added through compose overrides:
- **db** - MariaDB or PostgreSQL database server (via `compose.mariadb.yaml` or `compose.postgres.yaml`)
- **redis-cache/queue** - Redis instances for caching and job queues (via `compose.redis.yaml`)
### How Services Work Together
```
User Request
[frontend (Nginx)] → Static files served directly
[backend (Werkzeug)] → Dynamic content processing
↓ ↓
[db (MariaDB)] [redis-cache]
Background Tasks:
[scheduler] → [redis-queue] → [queue-short/long workers]
Real-time:
[websocket (Socket.IO)] ←→ [redis-cache]
```
## Repository Structure
### `/` Root: Core Configuration Files
- **compose.yaml** - Main Docker Compose file defining all services
- **example.env** - Environment variables template (copy to `.env`)
- **pwd.yml** - "Play with Docker" - simplified single-file setup for quick testing
- **docker-bake.hcl** - Advanced Docker Buildx configuration for multi-architecture builds
- **docs/container-setup/env-variables.md** - Central reference for environment configuration logic and defaults
### `images/`: Docker Image Definitions
Four predefined Dockerfiles are available, each serving different use cases:
- **images/bench/** - Sets up only the Bench CLI for development or debugging; does not include runtime services
- **images/custom/** - Multi-purpose Python backend built from plain Python base image; installs apps from `apps.json`; suitable for **production** and testing; ideal when you need control over Python/Node versions
- **images/layered/** - Same final contents as `custom` but based on prebuilt images from Docker Hub; faster builds for production when using Frappe-managed dependency versions
- **images/production/** - Installs only Frappe and ERPNext (not customizable with `apps.json`); best for **quick starts or exploration**; for real deployments, use `custom` or `layered`
> **Note:** For detailed build arguments and advanced configuration options, see [Setup Overview](../02-setup/01-overview.md).
### `overrides/`: Compose File Extensions
Docker Compose "overrides" that extend the base compose.yaml for different scenarios:
- **compose.mariadb.yaml** - Adds MariaDB database service
- **compose.redis.yaml** - Adds Redis caching service
- **compose.proxy.yaml** - Adds Traefik reverse proxy for multi-site hosting (label-based routing)
- **compose.https.yaml** - Adds Traefik HTTPS + automatic certs (uses `SITES_RULE`)
- **compose.nginxproxy.yaml** - Adds nginx-proxy reverse proxy (HTTP, env-based `VIRTUAL_HOST`)
- **compose.nginxproxy-ssl.yaml** - Adds nginx-proxy + acme-companion (HTTPS, env-based `LETSENCRYPT_HOST`)
**Proxy choice:**
- Traefik is more flexible for advanced routing and multi-bench setups
- nginx-proxy is simpler for a single bench with host-based routing.
### `development/`: Dev Environment
- **development/installer.py** - Automated bench/site creation and configuration script
- Contains your local development files (git-ignored to prevent accidental commits)
### `resources/`: Runtime Templates
- **core/nginx/nginx-entrypoint.sh** - Dynamic Nginx configuration generator script
- **core/nginx/nginx-template.conf** - Nginx configuration template with variable substitution

View file

@ -1,3 +1,7 @@
---
title: Choosing a Method
---
# Choosing a Deployment or Development Method
This repository (`frappe_docker`) supports **multiple ways to run Frappe using Docker**.
@ -45,7 +49,7 @@ If you start with `pwd.yml`, you should expect to **throw the environment away**
## 2. VS Code Devcontainers Local Development Setup
The development setup described in [`/docs/05-development`](../05-development)
The development setup described in [`/docs/05-development/development.md`](../05-development/01-development.md)
uses **VS Code Devcontainers** to provide a **local Frappe development environment**.
@ -103,7 +107,7 @@ It uses:
- The main `compose.yml`
- Override files from the `overrides/` directory
Detailed instructions are available in [`/docs/02-setup`](../02-setup)
Detailed instructions are available in [`/docs/02-setup`](../02-setup/01-overview.md)
### Characteristics

View file

@ -1,3 +1,7 @@
---
title: Docker Immutability
---
# Important Concept: Immutability and Persistence
A frequent source of confusion is how **Docker-based Frappe deployments handle persistence**.
@ -31,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

@ -1,3 +1,7 @@
---
title: Quick Start with Linux and Mac
---
# How to install ERPNext on linux/mac using Frappe_docker ?
## Clone the repo
@ -25,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
@ -35,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
@ -64,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
@ -96,8 +100,8 @@ services:
bench new-site --mariadb-user-host-login-scope=% --admin-password=admin --db-root-password=admin --install-app erpnext --set-default frontend;
db:
image: mariadb:10.6
platform: linux/amd64
image: mariadb:11.8
platform: linux/arm64
healthcheck:
test: mysqladmin ping -h localhost --password=admin
interval: 1s
@ -109,15 +113,14 @@ services:
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_unicode_ci
- --skip-character-set-client-handshake
- --skip-innodb-read-only-compressed # Temporary fix for MariaDB 10.6
environment:
MYSQL_ROOT_PASSWORD: admin
volumes:
- 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:
@ -141,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
@ -156,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
@ -172,7 +175,7 @@ services:
redis-queue:
image: redis:6.2-alpine
platform: linux/amd64
platform: linux/arm64
deploy:
restart_policy:
condition: on-failure
@ -181,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
@ -200,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

@ -1,3 +1,7 @@
---
title: Single Compose Setup
---
# Single Compose Setup
This setup is a very simple single compose file that does everything to start required services and a frappe-bench. It is used to start play with docker instance with a site. The file is located in the root of repo and named `pwd.yml`.

View file

@ -0,0 +1,3 @@
---
title: Getting Started
---

View file

@ -1,3 +1,7 @@
---
title: Setup Overview
---
The purpose of this document is to give you an overview of how the Frappe Docker containers are structured.
# 🐳 Images

View file

@ -1,13 +1,19 @@
---
title: Build Setup
---
This guide walks you through building Frappe images from the repository resources.
# Prerequisites
- git
- docker or podman
- docker (Engine **v23.0+** with buildx) or podman
- docker compose v2 or podman compose
> Install containerization software according to the official maintainer documentation. Avoid package managers when not recommended, as they frequently cause compatibility issues.
> **Why Docker Engine v23+?** The build uses [BuildKit secrets](https://docs.docker.com/build/building/secrets/) (`--secret`) to keep `apps.json` tokens out of image layers. BuildKit is the default builder starting with Docker Engine 23.0 — older releases will fail or silently fall back to the legacy builder, which does not support secret mounts.
# Clone this repo
```bash
@ -17,7 +23,7 @@ cd frappe_docker
# Define custom apps
If you dont want to install specific apps to the image skip this section.
If you don't want to include custom apps in the image, skip this section.
To include custom apps in your image, create an `apps.json` file in the repository root:
@ -25,11 +31,11 @@ To include custom apps in your image, create an `apps.json` file in the reposito
[
{
"url": "https://github.com/frappe/erpnext",
"branch": "version-15"
"branch": "version-16"
},
{
"url": "https://github.com/frappe/hrms",
"branch": "version-15"
"branch": "version-16"
},
{
"url": "https://github.com/frappe/helpdesk",
@ -38,24 +44,23 @@ To include custom apps in your image, create an `apps.json` file in the reposito
]
```
Then generate a base64-encoded string from this file:
# Build custom images
```bash
export APPS_JSON_BASE64=$(base64 -w 0 apps.json)
```
# Build the image
## Manually
Choose the appropriate build command based on your container runtime and desired image type. This example builds the `layered` image with the custom `apps.json` you created.
> **Security note:** The `apps.json` file is passed as a [BuildKit secret](https://docs.docker.com/build/building/secrets/) so that private repository tokens are **never** stored in image layer metadata. Do not use `--build-arg` for `apps.json` — build arguments are permanently visible via `docker image history`. This requires **Docker Engine v23.0+** (where BuildKit is the default builder).
`Docker`:
```bash
docker build \
--no-cache \
--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 \
--build-arg=FRAPPE_BRANCH=version-16 \
--secret=id=apps_json,src=apps.json \
--tag=custom:16 \
--file=images/layered/Containerfile .
```
@ -63,31 +68,42 @@ docker build \
```bash
podman build \
--no-cache \
--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 \
--build-arg=FRAPPE_BRANCH=version-16 \
--secret=id=apps_json,src=apps.json \
--tag=custom:16 \
--file=images/layered/Containerfile .
```
## Build args
## Automated
| Arg | Purpose |
| -------------------- | --------------------------------------------------------------------------------------------- |
| **Frappe Framework** | |
| FRAPPE_PATH | Repository URL for Frappe framework source code. Defaults to https://github.com/frappe/frappe |
| FRAPPE_BRANCH | Branch to use for Frappe framework. Defaults to version-15 |
| **Custom Apps** | |
| APPS_JSON_BASE64 | Base64-encoded JSON string from apps.json defining apps to install |
| **Dependencies** | |
| PYTHON_VERSION | Python version for the base image |
| NODE_VERSION | Node.js version |
| WKHTMLTOPDF_VERSION | wkhtmltopdf version |
| **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` |
This repository is fully suited for automated builds, i.e. using CI/CD pipelines.
# env file
See [Automated Builds and Deployment](../03-production/06-automated-builds-and-deployment.md) for more information.
## Build args, secrets and flags
| Variable | Purpose |
| -------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| **Frappe Framework** | |
| FRAPPE_PATH | Repository URL for Frappe framework source code. Defaults to <https://github.com/frappe/frappe> |
| FRAPPE_BRANCH | Branch to use for Frappe framework. Defaults to version-16 |
| **Custom Apps** | |
| CACHE_BUST | Can be used to invalidate the cached layer. See [Build Cache](../03-production/06-automated-builds-and-deployment.md#build-cache) |
| (secret) apps_json | Passed via `--secret=id=apps_json,src=apps.json`. Never use `--build-arg` for this file. |
| **Dependencies** | |
| 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` |
# Deploy the stack
## env file
The compose file requires several environment variables. You can either export them on your system or create a `.env` file.
@ -101,7 +117,7 @@ For this setup, make sure **at least** the following values are added to `custom
```txt
CUSTOM_IMAGE=custom
CUSTOM_TAG=15
CUSTOM_TAG=16
PULL_POLICY=missing
```
@ -111,7 +127,7 @@ PULL_POLICY=missing
**⚠️ This is not meant to be a complete `.env` configuration guide. These are only the minimal additions required for this example.
Please have a look at [env-variables.md](04-env-variables.md) for a full description of all available variables and adjust them according to your needs.**
# Creating the final compose file
## Creating the final compose file
Combine the base compose file with appropriate overrides for your use case. This example adds MariaDB, Redis, and exposes ports on `:8080`:

View file

@ -1,3 +1,7 @@
---
title: Start Container
---
# start Container
Once your compose file is ready, start all containers with a single command:

View file

@ -1,3 +1,7 @@
---
title: Environment Variables
---
# Environment Variables Reference
Environment variables configure your Frappe Docker setup. They can be set directly in the container or defined in a `.env` file referenced by Docker Compose.
@ -43,12 +47,14 @@ Then edit `.env` and set variables according to your needs.
---
## HTTPS & SSL Configuration
## Reverse Proxy and SSL (HTTPS) Configuration
| Variable | Purpose | Default | When to Set |
| ------------------- | ------------------------------------------------------------- | ------- | ---------------------------------------- |
| `LETSENCRYPT_EMAIL` | Email for Let's Encrypt certificate registration | — | Required if using HTTPS override |
| `SITES_RULE` | List of domains for SSL (Traefik rule for TLS domain routing) | — | Required if using reverse proxy override |
### Traefik (compose.proxy.yaml / compose.https.yaml)
| Variable | Purpose | Default | When to Set |
| ------------------- | ------------------------------------------------ | ------- | -------------------------------------------- |
| `LETSENCRYPT_EMAIL` | Email for Let's Encrypt certificate registration | - | Required for `compose.https.yaml` |
| `SITES_RULE` | Domains for routing (Traefik rule expression) | - | Required for Traefik routing/HTTPS overrides |
**Format for `SITES_RULE`:**
@ -63,6 +69,28 @@ SITES_RULE=Host(`a.example.com`) || Host(`b.example.com`)
> Note: The Traefik v3 migration is complete. Use `SITES_RULE` as a full v3 rule expression; `SITES` is deprecated.
> Rule syntax now defaults to v3, so no `core.defaultRuleSyntax` or per-router `ruleSyntax` settings are required.
### nginx-proxy + acme-companion (compose.nginxproxy\*.yaml)
| Variable | Purpose | Default | When to Set |
| ------------------- | ----------------------------------------- | ------- | ------------------------------------------ |
| `LETSENCRYPT_EMAIL` | Email for Let's Encrypt certificate | - | Required for `compose.nginxproxy-ssl.yaml` |
| `NGINX_PROXY_HOSTS` | Comma-separated hostnames for nginx-proxy | - | Required for `compose.nginxproxy*.yaml` |
**Example:**
```bash
NGINX_PROXY_HOSTS=example.com,www.example.com
```
> Note: Automatic certificates require port 80 to be reachable (HTTP-01).
### Published Ports (Traefik and nginx-proxy)
| Variable | Purpose | Default | When to Set |
| -------------------- | -------------------- | ------------------------------- | ---------------------------- |
| `HTTP_PUBLISH_PORT` | Published HTTP port | `80` (proxy) / `8080` (noproxy) | Change if port is in use |
| `HTTPS_PUBLISH_PORT` | Published HTTPS port | `443` | Change if port 443 is in use |
---
## Site Configuration
@ -94,13 +122,22 @@ If your site is named `example.com` and you access it via that domain, no need t
---
## Nginx Proxy Configuration
## 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 |
| ---------------------- | ---------------------------------- | -------------- | -------------------------------------------- |
| `BACKEND` | Backend service address and port | `0.0.0.0:8000` | `{host}:{port}` |
| `SOCKETIO` | Socket.IO service address and port | `0.0.0.0:9000` | `{host}:{port}` |
| `HTTP_PUBLISH_PORT` | Published HTTP port | `8080` | Any available port |
| `PROXY_READ_TIMEOUT` | Upstream request timeout | `120s` | Any nginx timeout value (e.g., `300s`, `5m`) |
| `CLIENT_MAX_BODY_SIZE` | Maximum upload file size | `50m` | Any nginx size value (e.g., `100m`, `1g`) |
@ -113,3 +150,11 @@ Use these variables when running behind a reverse proxy or load balancer:
| `UPSTREAM_REAL_IP_ADDRESS` | Trusted upstream IP address for real IP detection | `127.0.0.1` |
| `UPSTREAM_REAL_IP_HEADER` | Request header containing client IP | `X-Forwarded-For` |
| `UPSTREAM_REAL_IP_RECURSIVE` | Enable recursive IP search | `off` |
---
## Migration Service
| Variable | Purpose | Default | Allowed Values |
| --------------- | ------------------------------- | -------------------------- | ---------------- |
| `MIGRATE_SITES` | Switch auto migration on or off | `true` - auto migration on | `true` , `false` |

View file

@ -1,27 +1,35 @@
---
title: Overrides
---
Overrides extend the base compose.yaml with additional services or modify existing behavior. Include them in your compose command using multiple -f flags.
```bash
docker compose -f compose.yaml -f overrides/compose.mariadb.yaml -f overrides/compose.redis.yaml config > compose.custom.yaml
```
| Overrider | Purpose | Additional Info |
| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
| **Database** | | |
| compose.mariadb.yaml | Adds MariaDB database service | set `DB_PASSWORD` or default Password will be used |
| compose.mariadb-secrets.yaml | Adds MariaDB with password from a secret file instead of environment variable | Set `DB_PASSWORD_SECRETS_FILE` to the path of your secret file |
| compose.mariadb-shared.yaml | Makes MariaDB available on a shared network (mariadb-network) for other services | set `DB_PASSWORD` |
| compose.postgres.yaml | Uses PostgreSQL instead of MariaDB as the database | set `DB_PASSWORD` |
| **Proxy** | | |
| compose.noproxy.yaml | Exposes the application directly on port `:8080` without a reverse proxy | |
| compose.proxy.yaml | Uses Traefik as HTTP reverse proxy on port `:80` | You can change the published port by setting `HTTP_PUBLISH_PORT` |
| compose.https.yaml | Uses Traefik as HTTPS reverse proxy on Port `:443` with automatic HTTP-to-HTTPS redirect | `SITES_RULE` and `LETSENCRYPT_EMAIL` must be set. `HTTP_PUBLISH_PORT` and `HTTPS_PUBLISH_PORT` can be set. |
| **Redis** | | |
| compose.redis.yaml | Adds Redis service for caching and background job queuing |
| **TBD** | **The following overrides are available but lack documentation. If you use them and understand their purpose, please consider contributing to this documentation.** |
| compose.backup-cron.yaml | | |
| compose.custom-domain-ssl.yaml | | |
| compose.custom-domain.yaml | | |
| compose.multi-bench-ssl.yaml | | |
| compose.multi-bench.yaml | | |
| compose.traefik-ssl.yaml | | |
| compose.traefik.yaml | | |
| Overrider | Purpose | Additional Info |
| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| **Database** | | |
| compose.mariadb.yaml | Adds MariaDB database service | set `DB_PASSWORD` or default Password will be used |
| compose.mariadb-secrets.yaml | Adds MariaDB with password from a secret file instead of environment variable | Set `DB_PASSWORD_SECRETS_FILE` to the path of your secret file |
| compose.mariadb-shared.yaml | Makes MariaDB available on a shared network (mariadb-network) for other services | set `DB_PASSWORD` |
| compose.postgres.yaml | Uses PostgreSQL instead of MariaDB as the database | set `DB_PASSWORD` |
| **Proxy** | | |
| compose.noproxy.yaml | Exposes the application directly on port `:8080` without a reverse proxy | |
| compose.proxy.yaml | Uses Traefik as HTTP reverse proxy on port `:80` | You can change the published port by setting `HTTP_PUBLISH_PORT` |
| compose.https.yaml | Uses Traefik as HTTPS reverse proxy on Port `:443` with automatic HTTP-to-HTTPS redirect | `SITES_RULE` and `LETSENCRYPT_EMAIL` must be set. `HTTP_PUBLISH_PORT` and `HTTPS_PUBLISH_PORT` can be set. |
| compose.traefik.yaml | Runs a standalone Traefik proxy with dashboard (HTTP) on a shared `traefik-public` network | Use for multi-stack setups. Requires `TRAEFIK_DOMAIN` and `HASHED_PASSWORD`. |
| compose.traefik-ssl.yaml | Adds HTTPS and Let's Encrypt for the Traefik dashboard | Use with `compose.traefik.yaml`. Requires `EMAIL` and `TRAEFIK_DOMAIN`. Publishes `HTTPS_PUBLISH_PORT`. |
| compose.nginxproxy.yaml | Uses nginx-proxy as HTTP reverse proxy on port `:80` | Set `NGINX_PROXY_HOSTS`. Use with `compose.nginxproxy-ssl.yaml` for HTTPS. You can change the published port by setting `HTTP_PUBLISH_PORT` |
| compose.nginxproxy-ssl.yaml | Adds acme-companion for HTTPS on port `:443` with automatic certificates | Requires `compose.nginxproxy.yaml`. Set `NGINX_PROXY_HOSTS` and `LETSENCRYPT_EMAIL`. `HTTP_PUBLISH_PORT` and `HTTPS_PUBLISH_PORT` can be set. |
| **Redis** | | |
| compose.redis.yaml | Adds Redis service for caching and background job queuing | |
| **Services** | | |
| compose.migrator.yaml | Runs a dedicated migration container performing `bench --site all migrate` on all sites at every start | Control migration intent with `MIGRATE_SITES` - defaults to true |
| **TBD** | **The following overrides are available but lack documentation. If you use them and understand their purpose, please consider contributing to this documentation.** | |
| compose.backup-cron.yaml | | |
| compose.custom-domain-ssl.yaml | | |
| compose.custom-domain.yaml | | |
| compose.multi-bench-ssl.yaml | | |
| compose.multi-bench.yaml | | |

View file

@ -1,3 +1,7 @@
---
title: Setup Examples
---
# Setup Examples
This guide provides practical examples for common setup scenarios. These examples build upon the [container setup guide](01-overview.md) and demonstrate how to combine the base compose file with overrides.

View file

@ -1,3 +1,7 @@
---
title: Single Server Setup
---
# Single Server Example
This guide demonstrates setting up multiple Frappe/ERPNext benches (projects) on a single server with shared infrastructure components.

View file

@ -0,0 +1,173 @@
---
title: Single Server Example
---
# Single Server Example (nginx-proxy + acme-companion)
This guide demonstrates a single-server setup using nginx-proxy and acme-companion for HTTPS. It is best for a small number of hostnames and a single bench. If you need multiple benches or advanced routing, use the Traefik-based example instead.
We will setup the following:
- Install Docker and Docker Compose v2 on a Linux server.
- Use nginx-proxy + acme-companion for HTTPS (Let's Encrypt).
- Install MariaDB and Redis via containers.
- Setup one project called `erpnext` with sites `erp.your-domain.com` and `crm.your-domain.com`.
## Requirements
- A server that can run Docker Engine **v23.0+** (recommended: 2 vCPU, 4 GB RAM, 50 GB SSD). The custom-image build below uses [BuildKit secrets](https://docs.docker.com/build/building/secrets/), which require BuildKit as the default builder (Docker Engine 23.0+).
- A public domain with DNS control.
- Two subdomains pointing to your server IP (A/AAAA records):
- `erp.your-domain.com`
- `crm.your-domain.com`
- Ports 80 and 443 reachable from the internet (required for Let's Encrypt HTTP-01).
### Install Docker
Docker can be installed on a variety of systems. The easiest way to do this is with the convenience script.
| Platform | Convenience script | Using repository |
| -------- | ------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- |
| CentOS | [Link](https://docs.docker.com/engine/install/centos/#install-using-the-convenience-script) | [Link](https://docs.docker.com/engine/install/centos/#install-using-the-repository) |
| Debian | [Link](https://docs.docker.com/engine/install/debian/#install-using-the-convenience-script) | [Link](https://docs.docker.com/engine/install/debian/#install-using-the-repository) |
| Ubuntu | [Link](https://docs.docker.com/engine/install/ubuntu/#install-using-the-convenience-script) | [Link](https://docs.docker.com/engine/install/ubuntu/#install-using-the-repository) |
| Fedora | [Link](https://docs.docker.com/engine/install/fedora/#install-using-the-convenience-script) | [Link](https://docs.docker.com/engine/install/fedora/#install-using-the-repository) |
Then do the post-installation steps. This will ensure that the permissions are easier to use and that Docker will start up with the System. [Post-Installation Steps](https://docs.docker.com/engine/install/linux-postinstall/)
### Prepare
Clone `frappe_docker` and change the current working directory to the repo.
```shell
git clone https://github.com/frappe/frappe_docker
cd frappe_docker
```
Create a configuration directory:
```shell
mkdir ~/gitops
```
## Optional: Build a custom image
If you need extra apps (beyond Frappe/ERPNext), build a custom image. Otherwise, skip this section and use the default images.
Create `apps.json` (each entry is a Git repo + branch):
```shell
cat > ~/gitops/apps.json <<'EOF'
[
{
"url": "https://github.com/frappe/erpnext",
"branch": "version-16"
},
{
"url": "https://github.com/frappe/payments",
"branch": "version-16"
}
]
EOF
```
Example for CRM only:
```shell
cat > ~/gitops/apps.json <<'EOF'
[
{
"url": "https://github.com/frappe/crm",
"branch": "main"
}
]
EOF
```
Build the image, passing `apps.json` as a [BuildKit secret](https://docs.docker.com/build/building/secrets/) so that private repo tokens are never stored in image layers. This requires **Docker Engine v23.0+**, where BuildKit is the default builder:
```shell
docker build \
--build-arg=FRAPPE_PATH=https://github.com/frappe/frappe \
--build-arg=FRAPPE_BRANCH=version-16 \
--secret=id=apps_json,src=$HOME/gitops/apps.json \
--tag=my-erpnext-prod-image:16.0.0 \
--file=images/layered/Containerfile .
```
### Configure environment
Create an environment file for the bench:
```shell
cp example.env ~/gitops/erpnext.env
sed -i 's/DB_PASSWORD=123/DB_PASSWORD=changeit/g' ~/gitops/erpnext.env
echo 'NGINX_PROXY_HOSTS=erp.your-domain.com,crm.your-domain.com' >> ~/gitops/erpnext.env
echo 'LETSENCRYPT_EMAIL=admin@your-domain.com' >> ~/gitops/erpnext.env
```
Notes:
- Replace `changeit` with a strong password.
- Replace domains and email with your production values.
- `NGINX_PROXY_HOSTS` is a comma-separated list without spaces.
- If you built a custom image, add:
```shell
echo "CUSTOM_IMAGE=my-erpnext-prod-image" >> ~/gitops/erpnext.env
echo "CUSTOM_TAG=16.0.0" >> ~/gitops/erpnext.env
```
### Generate compose config
Create the rendered compose file:
```shell
docker compose --project-name erpnext \
--env-file ~/gitops/erpnext.env \
-f compose.yaml \
-f overrides/compose.mariadb.yaml \
-f overrides/compose.redis.yaml \
-f overrides/compose.nginxproxy.yaml \
-f overrides/compose.nginxproxy-ssl.yaml config > ~/gitops/erpnext.yaml
```
Start the stack:
```shell
docker compose --project-name erpnext -f ~/gitops/erpnext.yaml up -d
```
This starts MariaDB and Redis containers as part of the same stack.
### Create sites
```shell
# erp.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 erp.your-domain.com
# 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 crm --admin-password changeit crm.your-domain.com
```
### Notes
- Let's Encrypt requires ports 80 and 443 to be reachable from the internet.
- If you cannot expose these ports (LAN-only), omit `compose.nginxproxy-ssl.yaml` and use HTTP or a local TLS proxy like Caddy.
- Replace `changeit` with a strong DB root password and set a strong admin password per site.
### Site operations
Refer: [site operations](../04-operations/01-site-operations.md)
### Troubleshooting (ACME / certificates)
- **No certificate issued:** Verify DNS points to the server IP and ports 80/443 are reachable from the internet.
- **ACME errors in logs:** Check `acme-companion` logs for the exact challenge error.
- **Wrong hostname:** Ensure the domain is included in `NGINX_PROXY_HOSTS` and that you restarted the stack after edits.
---
**Back:** [Single Server Example (Traefik)](07-single-server-example.md)

3
docs/02-setup/index.md Normal file
View file

@ -0,0 +1,3 @@
---
title: Setup
---

View file

@ -1,25 +1,57 @@
# Accessing ERPNext through https on local deployment
---
title: TLS/SSL Setup Overview
---
- ERPNext container deployment can be accessed through https easily using Caddy web server, Caddy will be used as reverse proxy and forward traffics to the frontend container.
# TLS/SSL Setup Overview
### Prerequisites
Frappe Docker supports multiple TLS/SSL approaches. Choose the one that matches your routing needs and where you want the proxy to run.
- Caddy
- Adding a domain name to hosts file
## Options
#### Installation of caddy webserver
### Traefik (built-in HTTPS)
- Follow the official Caddy website for the installation guide https://caddyserver.com/docs/install
After completing the installation open the configuration file of Caddy ( You find the config file in ` /etc/caddy/Caddyfile`), add the following configuration to forward traffics to the ERPNext frontend container
- Use `overrides/compose.https.yaml`
- Best for multi-site setups and advanced routing rules
- Requires `SITES_RULE` and `LETSENCRYPT_EMAIL`
- See [Environment Variables](../02-setup/04-env-variables.md) and [Setup Examples](../02-setup/06-setup-examples.md#example-3-production-setup-with-https)
```js
erp.localdev.net {
tls internal
#### Traefik deployment models
reverse_proxy localhost:8085 {
- **Single stack (Traefik inside the stack):**
- Use `compose.proxy.yaml` (HTTP) or `compose.https.yaml` (HTTPS)
- Traefik runs as `proxy` in the same stack
- **Central Traefik for multiple stacks:**
- Run a dedicated Traefik stack with `compose.traefik.yaml` (and optional `compose.traefik-ssl.yaml` for the dashboard)
- Each Frappe stack uses `compose.multi-bench.yaml` (and optional `compose.multi-bench-ssl.yaml`)
- This connects stacks to the shared `traefik-public` network
}
}
```
### nginx-proxy + acme-companion
- Caddy's root certificate must be added to other computers if computers from different networks access the ERPNext through https.
- Use `overrides/compose.nginxproxy.yaml` plus `overrides/compose.nginxproxy-ssl.yaml`
- Simple host-based routing for single-bench or small setups
- Requires `NGINX_PROXY_HOSTS` and `LETSENCRYPT_EMAIL`
- See [nginx-proxy + acme-companion](04-nginx-proxy-acme-companion.md)
## Traefik vs nginx-proxy + acme-companion
| Topic | Traefik (compose.https.yaml) | nginx-proxy + acme-companion |
| ------------------- | --------------------------------------------- | ------------------------------------------------------------------------------ |
| Configuration | Labels with `SITES_RULE` expression | Environment variables (`NGINX_PROXY_HOSTS`) |
| Routing | Flexible (rules, headers, paths) | Host-based only |
| Multi-site | Strong | Works for simple host lists |
| TLS/ACME | Built-in | Separate companion container |
| Certificate storage | `cert-data` volume (`/letsencrypt/acme.json`) | `nginx-proxy-certs` + `acme-data` volumes (`/etc/nginx/certs`, `/etc/acme.sh`) |
| Complexity | Moderate | Low |
| Observability | Optional dashboard (not enabled here) | No built-in dashboard |
### Caddy (external reverse proxy)
- Run Caddy on the host and proxy to the frontend container
- Useful for local HTTPS or when you already use Caddy
- See [Caddy reverse proxy](05-caddy-https.md)
## Common requirements
- DNS must point to the server for public TLS certificates
- Ports 80 and 443 must be reachable for HTTP-01 challenges
- Use `HTTP_PUBLISH_PORT` and `HTTPS_PUBLISH_PORT` if you need non-default ports

View file

@ -1,3 +1,7 @@
---
title: Backup Strategy
---
Create backup service or stack.
```yaml

View file

@ -1,3 +1,7 @@
---
title: Multi Tenancy
---
WARNING: Do not use this in production if the site is going to be served over plain http.
### Step 1

View file

@ -0,0 +1,86 @@
---
title: NGINX and ACME Companion
---
# nginx-proxy + acme-companion (HTTPS)
This guide explains how to use nginx-proxy with acme-companion to provide HTTPS for a Frappe Docker stack.
## When to choose this
- You want a simple, host-based reverse proxy
- You run a single bench or only a few hostnames
- You prefer environment-variable based configuration
If you need advanced routing or complex multi-site setups, **Traefik** is usually the better choice.
## Prerequisites
- Public DNS points your domain(s) to the server
- Ports 80 and 443 are reachable (HTTP-01 challenge)
- Docker and Docker Compose v2 installed
## Required environment variables
Set these in `.env`:
```bash
NGINX_PROXY_HOSTS=erp.your-domain.com
LETSENCRYPT_EMAIL=admin@your-domain.com
```
Multiple hostnames (comma-separated, no spaces):
```bash
NGINX_PROXY_HOSTS=erp.your-domain.com,erp2.your-domain.com
LETSENCRYPT_EMAIL=admin@example.com
```
Optional (non-default ports):
```bash
HTTP_PUBLISH_PORT=80
HTTPS_PUBLISH_PORT=443
```
## Compose setup (HTTPS)
For HTTPS you must include both overrides:
- `overrides/compose.nginxproxy.yaml` (nginx-proxy, VIRTUAL_HOST)
- `overrides/compose.nginxproxy-ssl.yaml` (acme-companion, LETSENCRYPT_HOST)
Example:
```sh
docker compose -f compose.yaml \
-f overrides/compose.mariadb.yaml \
-f overrides/compose.redis.yaml \
-f overrides/compose.nginxproxy.yaml \
-f overrides/compose.nginxproxy-ssl.yaml \
config > ~/gitops/docker-compose.yml
docker compose --project-name <project-name> -f ~/gitops/docker-compose.yml up -d
```
> If you use external MariaDB/Redis, replace the database and Redis overrides accordingly.
## How hostnames are applied
`NGINX_PROXY_HOSTS` is a comma-separated list of hostnames. The overrides apply it as:
- `VIRTUAL_HOST` for nginx-proxy routing
- `LETSENCRYPT_HOST` for certificate issuance
## Verify
Check logs for certificate issuance and proxy status:
```sh
docker compose --project-name <project-name> -f ~/gitops/docker-compose.yml logs -f nginx-proxy
docker compose --project-name <project-name> -f ~/gitops/docker-compose.yml logs -f acme-companion
```
> Depending on the registrar, the assignment may take some time, whereby it must also be ensured that A and AAAA records are correctly directed to the server for the issuance of the certificate, if necessary.
See also: [Environment Variables](../02-setup/04-env-variables.md) and [TLS/SSL Setup Overview](01-tls-ssl-setup.md).

View file

@ -0,0 +1,48 @@
---
title: Caddy with HTTPS
---
# Caddy reverse proxy (local HTTPS)
This guide shows how to use Caddy as an external reverse proxy in front of the frontend container. It is most useful for local HTTPS or internal networks.
## Prerequisites
- Expose the frontend container on a host port (default 8080)
- Add a local domain to your hosts file (or use internal DNS)
- Install Caddy
## Step 1: Expose the frontend service
Include the no-proxy override so the frontend is reachable on the host:
```sh
docker compose -f compose.yaml \
-f overrides/compose.mariadb.yaml \
-f overrides/compose.redis.yaml \
-f overrides/compose.noproxy.yaml \
config > ~/gitops/docker-compose.yml
docker compose --project-name <project-name> -f ~/gitops/docker-compose.yml up -d
```
If you changed the HTTP port, note the value of `HTTP_PUBLISH_PORT` for the next step.
## Step 2: Configure Caddy
Add a site block to your Caddyfile (usually `/etc/caddy/Caddyfile`):
```caddy
erp.localdev.net {
tls internal
reverse_proxy localhost:8080
}
```
Replace `8080` with your published frontend port if you changed it.
## Step 3: Trust the Caddy root certificate
When using `tls internal`, Caddy issues certificates from its internal CA. Import and trust the Caddy root certificate on any client that needs to access the site.
See also: [TLS/SSL Setup Overview](01-tls-ssl-setup.md).

View file

@ -0,0 +1,147 @@
---
title: Automated Builds and Deployment
---
# Introduction
This is a brief guide to automated builds and deployment for custom Frappe images.
Depending on your specific setup, environment and security rules, the information below may need to be adapted to your needs.
# Requirements
## Knowledge
Basic knowledge of Docker and build pipelines is expected.
Please refer to the Setup chapter first, especially [Build Setup](../02-setup/02-build-setup.md), for basic understanding.
## Additional Files
### Apps
At build time an `apps.json` file can be provided. This specifies additional Frappe framework compatible apps to include in custom images.
### Build
A workflow file for your CI platform and environment is required.
## Build Cache
Unlike manual builds, automated build commands should generally not use `--no-cache`.
Reusing cached layers can greatly reduce build times, disk usage, and bandwidth usage when pushing to image registries.
Instead, `CACHE_BUST` can be used to control cache invalidation of the Frappe layer when rebuilding is desired.
This is especially relevant because `apps.json` is provided as a secret. Secret contents are not part of Docker layer cache keys and therefore cannot trigger cache invalidation automatically.
As a result, Docker may reuse an older cached layer even when the custom app definition has changed.
Exception: Newer releases of the Frappe framework may still trigger rebuilding the layer.
### Possible techniques for cache invalidation using `CACHE_BUST`:
1. No override: normal Docker layer caching is used - not recommended in this use case
2. Timestamp: force a rebuild on every pipeline run - since the value will change every run
3. Pipeline run ID: rebuild once per CI run
4. Commit SHA: rebuild once per commit
5. apps.json hash: rebuild only when the custom app definition changes - additional requirements, see below example
### Examples:
#### 1. No override - not recommended
This will reuse a previously build layer and won't check for app updates except Frappe framework
```yaml
- name: Build Docker image
shell: sh
run: |
docker build \
--build-arg=FRAPPE_PATH=https://github.com/frappe/frappe \
--build-arg=FRAPPE_BRANCH=version-16 \
--secret=id=apps_json,src=apps.json \
--tag=custom:16 \
--file=images/layered/Containerfile .
```
#### 2. Timestamp
```yaml
- name: Build Docker image
shell: sh
run: |
docker build \
--build-arg=FRAPPE_PATH=https://github.com/frappe/frappe \
--build-arg=FRAPPE_BRANCH=version-16 \
--build-arg=CACHE_BUST="$(date +%s)" \
--secret=id=apps_json,src=apps.json \
--tag=custom:16 \
--file=images/layered/Containerfile .
```
#### 3. Pipeline run ID from GitHub
```yaml
- name: Build Docker image
shell: sh
run: |
docker build \
--build-arg=FRAPPE_PATH=https://github.com/frappe/frappe \
--build-arg=FRAPPE_BRANCH=version-16 \
--build-arg=CACHE_BUST="$GITHUB_RUN_ID" \
--secret=id=apps_json,src=apps.json \
--tag=custom:16 \
--file=images/layered/Containerfile .
```
#### 4. Commit SHA from GitHub
```yaml
- name: Build Docker image
shell: sh
run: |
docker build \
--build-arg=FRAPPE_PATH=https://github.com/frappe/frappe \
--build-arg=FRAPPE_BRANCH=version-16 \
--build-arg=CACHE_BUST="$GITHUB_SHA" \
--secret=id=apps_json,src=apps.json \
--tag=custom:16 \
--file=images/layered/Containerfile .
```
#### 5. apps.json hash
Note: When using branch references in `apps.json`, the hash only changes when the file content changes, not when an upstream app branch receives updates. This method works best when pinning specific commits or releases.
```yaml
- name: Build Docker image
shell: sh
run: |
docker build \
--build-arg=FRAPPE_PATH=https://github.com/frappe/frappe \
--build-arg=FRAPPE_BRANCH=version-16 \
--build-arg=CACHE_BUST="$(sha256sum apps.json | awk '{print $1}')" \
--secret=id=apps_json,src=apps.json \
--tag=custom:16 \
--file=images/layered/Containerfile .
```
## Automated deployment
### Automate site migration
After updating a custom image or deploying new app versions, a database migration
must be executed using `bench migrate`.
Without running migrations, the site may become inconsistent or fail to start properly.
For automated deployments, this step should not be performed manually.
Consider using the dedicated `migrator` service provided as a Compose override.
It ensures that migrations are executed automatically when the stack starts.
This approach is especially useful in CI/CD pipelines where no interactive access
to the backend container is available.
See [Compose override](../../overrides/compose.migrator.yaml)

View file

@ -0,0 +1,3 @@
---
title: Production
---

View file

@ -1,3 +1,7 @@
---
title: Site Operations
---
# Site operations
> 💡 You should setup `--project-name` option in `docker-compose` commands if you have non-standard project name.
@ -15,17 +19,12 @@ docker-compose exec backend bench new-site --mariadb-user-host-login-scope=% --d
If you need to install some app, specify `--install-app`. To see all options, just run `bench new-site --help`.
To create Postgres site (assuming you already use [Postgres compose override](../02-setup/05-overrides.md)) you need have to do set `root_login` and `root_password` in common config before that:
To create a Postgres site (assuming you already use [Postgres compose override](../02-setup/05-overrides.md)), the `root_login` and `root_password` are now automatically set by the `configurator`. For major version upgrades, please refer to the [Postgres Migration Guide](../06-migration/03-postgres-major-version-upgrade.md).
To create a new Postgres site:
```sh
docker-compose exec backend bench set-config -g root_login <root-login>
docker-compose exec backend bench set-config -g root_password <root-password>
```
Also command is slightly different:
```sh
docker-compose exec backend bench new-site --mariadb-user-host-login-scope=% --db-type postgres --admin-password <admin-password> <site-name>
docker-compose exec backend bench new-site --db-type postgres --admin-password <admin-password> <site-name>
```
## Push backup to S3 storage

View file

@ -0,0 +1,3 @@
---
title: Operations
---

View file

@ -1,3 +1,7 @@
---
title: Getting Started
---
# Getting Started
## Prerequisites
@ -97,6 +101,26 @@ PYENV_VERSION=3.9.17 bench init --skip-redis-config-generation --frappe-branch v
cd frappe-bench
```
At this point the the directory structure will be very close to this, if not exact,
```
development/
├── frappe-bench/ # Your actual Frappe installation
│ ├── apps/ # All installed Frappe applications
│ │ ├── frappe/ # Core framework (don't modify directly)
│ │ ├── erpnext/ # ERPNext application (if installed)
│ │ └── my_custom_app/ # Your custom apps (edit freely)
│ ├── sites/ # Multi-tenant sites
│ │ ├── development.localhost/ # Default dev site
│ │ │ ├── site_config.json # Site-specific config
│ │ │ └── private/files/ # Uploaded files
│ │ └── common_site_config.json # Shared configuration
│ ├── env/ # Python virtual environment
│ ├── logs/ # Application logs
│ └── config/ # Bench-level configuration
└── .vscode/ # VSCode workspace settings
```
### Setup hosts
We need to tell bench to use the right containers instead of localhost. Run the following commands inside the container:

View file

@ -1,3 +1,7 @@
---
title: Debugging
---
Add the following configuration to `launch.json` `configurations` array to start bench console and use debugger. Replace `development.localhost` with appropriate site. Also replace `frappe-bench` with name of the bench directory.
```json

View file

@ -1,3 +1,7 @@
---
title: Local Services
---
Add following to frappe container from the `.devcontainer/docker-compose.yml`:
```yaml

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,3 @@
---
title: Development
---

View file

@ -1,3 +1,7 @@
---
title: Migrate from Multi Image Setup
---
## Migrate from multi-image setup
All the containers now use same image. Use `frappe/erpnext` instead of `frappe/frappe-worker`, `frappe/frappe-nginx` , `frappe/frappe-socketio` , `frappe/erpnext-worker` and `frappe/erpnext-nginx`.
@ -110,3 +114,14 @@ create-site:
# ... removed for brevity
```
## Upgrading from images with a nested sites/assets volume
Previous images declared `VOLUME /home/frappe/frappe-bench/sites/assets` separately. This created an implicit nested mountpoint inside the `sites` volume, which could cause Docker to attach different anonymous volumes per container in multi-container setups.
That declaration has been removed. `sites` is now the single shared mount, consistent with the compose setup and docs.
**After pulling the updated image:**
- Recreate all containers (`docker compose up --force-recreate`). Without this, Docker may keep the old anonymous `sites/assets` volume
attached from before the change.
- No `bench build` is needed — this only fixes mount consistency, not the asset workflow.

View file

@ -1,3 +1,7 @@
---
title: Migrate Traefik from v2 to v3
---
# Migrate an existing Traefik v2 instance to v3
Use this guide if you already run Traefik v2 with `frappe_docker` and want to upgrade to v3. It focuses on the image upgrade and the v3 routing rule changes that affect existing setups.

View file

@ -0,0 +1,49 @@
---
title: Postgres Major Version Upgrade
---
# PostgreSQL Major Version Upgrade (v13 to v15)
Upgrading PostgreSQL from version 13 to 15 is a major version jump. Since PostgreSQL does not support in-place data directory upgrades, existing users must manually migrate their data using `pg_dump`.
### **Migration Steps**
1. **Backup Existing Data (Version 13):**
Before updating your compose file, ensure your containers are running and perform a dump of all databases.
```bash
docker exec -it <project_name>-db-1 pg_dumpall -U postgres > full_dump.sql
```
2. **Stop and Remove Containers:**
```bash
docker compose down
```
3. **Delete Old Data Volume:**
PostgreSQL 15 cannot read data created by version 13. You must remove the existing volume (Warning: this deletes the old data directory).
```bash
docker volume rm <project_name>_db-data
```
4. **Update Image and Start (Version 15):**
Update your `overrides/compose.postgres.yaml` (or pull the latest changes) and start the containers.
```bash
docker compose up -d
```
5. **Restore Data:**
Restore the dump into the new PostgreSQL 15 instance.
```bash
cat full_dump.sql | docker exec -i <project_name>-db-1 psql -U postgres
```
6. **Verify and Clean Up:**
Ensure your sites are working correctly with `bench migrate` and then remove the `full_dump.sql` file.
```bash
docker exec -it <project_name>-backend-1 bench --site all migrate
```

View file

@ -0,0 +1,3 @@
---
title: Migration
---

View file

@ -1,6 +1,11 @@
1. [Fixing MariaDB issues after rebuilding the container](#fixing-mariadb-issues-after-rebuilding-the-container)
1. [docker-compose does not recognize variables from `.env` file](#docker-compose-does-not-recognize-variables-from-env-file)
1. [Windows Based Installation](#windows-based-installation)
---
title: Troubleshoot
---
- [Fixing MariaDB issues after rebuilding the container](#fixing-mariadb-issues-after-rebuilding-the-container)
- [docker-compose does not recognize variables from `.env` file](#docker-compose-does-not-recognize-variables-from-env-file)
- [Windows Based Installation](#windows-based-installation)
- [Redo installation](#redo-installation)
### Fixing MariaDB issues after rebuilding the container

View file

@ -1,3 +1,7 @@
---
title: NGINX in Windows
---
# Resolving Docker `nginx-entrypoint.sh` Script Not Found Error on Windows
If you're encountering the error `exec /usr/local/bin/nginx-entrypoint.sh: no such file or directory` in a Docker container on Windows, follow these steps to resolve the issue.
@ -8,5 +12,5 @@ On Windows, files often have `CRLF` line endings, while Linux systems expect `LF
- **Convert Line Endings using `dos2unix`:**
```bash
dos2unix resources/nginx-entrypoint.sh
dos2unix resources/core/nginx/nginx-entrypoint.sh
```

View file

@ -0,0 +1,10 @@
---
title: ARM64 / Apple Silicon
---
## Notes on ARM64 and Apple Silicon
- Enable Docker Desktop's Rosetta emulation for initial builds when running on Apple Silicon with x86-only images.
- Prefer published multi-arch images (`frappe/bench`, `frappe/erpnext`) or build locally with `docker buildx bake --set *.platform=linux/amd64,linux/arm64` to cover both architectures in one pass.
- When using `pwd.yml`, export `DOCKER_DEFAULT_PLATFORM=linux/arm64` (or select the provided compose profile) to avoid unexpected emulation.
- Keep bind mounts under your user home directory and apply `:cached` or `:delegated` consistency flags for better performance on macOS.

View file

@ -0,0 +1,3 @@
---
title: Troubleshooting
---

View file

@ -1,3 +1,7 @@
---
title: Build Version 10
---
Clone the version-10 branch of this repo
```shell

View file

@ -0,0 +1,45 @@
---
title: Configuring VitePress
---
# Configuring VitePress
To modify any VitePress related settings, a JavaScript development environment is needed. Everything related to VitePress is contained in the `docs/` folder.
## Prerequisites
1. Node.js v24 or above is recommended. To install and manage Node.js [nvm](https://github.com/nvm-sh/nvm) is the preferred way for Linux and MacOS. For Windows either official installer or [fnm](https://github.com/Schniz/fnm).
2. pnpm package manager, v10.28 or above. Easiest way to install pnpm is using [corepack](https://pnpm.io/installation#using-corepack) which is part of Node.js.
## Development
To start a development environment,
1. Navigate to `/docs` directory in the terminal
```sh
cd docs
```
2. Install dependencies
```sh
pnpm install
```
3. Start the development server
```sh
pnpm run docs:dev
```
4. Open `http://localhost:5173/frappe_docker` in your browser to see the development version which will update the preview as you make changes.
## Configurations
1. Public assets related to VitePress site is added in the `docs/public` folder. This folder should **NOT** be used for adding images added inside the `.md` file.
2. VitePress uses `index.md` files to do some special things. For example the home page is configured using the `docs/index.md` file. Checkout the file for more details.
3. VitePress uses 'file based routing', meaning the URL paths mimics the directory and file structure inside the `docs/` directory.
4. VitePress specific config is `docs/.vitepress/config.mts`.
5. To auto populate the sidebar, a plugin called 'VitePress Sidebar' is used. The `config.mts` also include config for this plugin. More details can be found in the [documentation page](https://vitepress-sidebar.cdget.com/guide/getting-started).
6. Each subfolder has an `index.md` file. This is used to specify the group heading of the pages in the sidebar.

View file

@ -0,0 +1,161 @@
---
title: Fork Management
---
# Fork Management Best Practices
## Initial Fork Setup
```bash
# 1. Fork on GitHub (use the Fork button)
# 2. Clone YOUR fork
git clone https://github.com/YOUR_USERNAME/frappe_docker
cd frappe_docker
# 3. Add upstream remote (original repo)
git remote add upstream https://github.com/frappe/frappe_docker.git
# 4. Verify remotes
git remote -v
# origin https://github.com/YOUR_USERNAME/frappe_docker (your fork)
# upstream https://github.com/frappe/frappe_docker (original)
# 5. Create development branch
git checkout -b my-custom-setup
```
## Safe Customization Zones
**✅ Safe (Won't conflict with upstream):**
```
development/ # Your entire dev environment
├── frappe-bench/ # Local installation
└── .vscode/ # Your editor settings
compose.my-*.yaml # Your custom compose overrides
scripts/my-*.sh # Your custom scripts
docs/my-*.md # Your custom documentation
.env.local # Local environment overrides
.gitignore.local # Additional gitignore rules
```
**⚠️ Modification Needed (May conflict):**
```
compose.yaml # Core - use overrides instead
docker-bake.hcl # Build config - use custom files
images/*/Dockerfile # Core images - extend rather than modify
```
**❌ Never Modify (Will break upstream sync):**
```
.github/workflows/ # CI/CD pipelines
images/*/ # Core image definitions
resources/ # Core templates
```
## Keeping Fork Updated
```bash
# Regularly sync with upstream (weekly recommended)
git checkout main
git fetch upstream
git merge upstream/main
git push origin main
# Update your development branch
git checkout my-custom-setup
git rebase main # Or: git merge main
# If conflicts occur during rebase:
# 1. Fix conflicts in files
# 2. git add <fixed-files>
# 3. git rebase --continue
# Or: git rebase --abort (to cancel)
```
## Custom Environment Pattern
Create override files for your customizations:
```yaml
# compose.my-env.yaml
version: "3.7"
services:
backend:
environment:
# Your custom environment variables
- DEVELOPER_MODE=true
- MY_API_KEY=${MY_API_KEY}
volumes:
# Your custom bind mounts
- ./development/my-scripts:/home/frappe/my-scripts
- ./development/my-config:/home/frappe/config
# Your additional services
my-monitoring:
image: prom/prometheus
ports:
- "9090:9090"
volumes:
- ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml
# Use it:
# docker compose -f compose.yaml -f compose.my-env.yaml up
```
## .gitignore Strategy
Add to `.gitignore` (or create `.gitignore.local`):
```gitignore
# Local environment files
.env.local
*.local.yaml
compose.my-*.yaml
# Development artifacts
development/frappe-bench/sites/*
development/frappe-bench/apps/*
!development/frappe-bench/apps.json
development/frappe-bench/logs/
development/frappe-bench/env/
# Local customizations
my-local-configs/
scripts/my-*.sh
docs/internal-*.md
# IDE
.vscode/settings.json.local
.idea/
# Temporary files
*.swp
*.swo
*~
.DS_Store
```
## Contributing Back to Upstream
```bash
# 1. Create feature branch from main
git checkout main
git pull upstream main
git checkout -b feature/my-improvement
# 2. Make changes and commit
git add .
git commit -m "feat: add awesome feature"
# 3. Push to YOUR fork
git push origin feature/my-improvement
# 4. Create Pull Request on GitHub
# Go to: https://github.com/frappe/frappe_docker
# Click "Compare & pull request"
```

View file

@ -0,0 +1,163 @@
---
title: Framework Comparisons
---
# Framework Comparisons
> **Note:** This section provides comparisons to other frameworks for developers familiar with them. If you're new to all frameworks, you can skip this section - the rest of the guide is self-contained.
## Frappe vs Django Concepts
### Project Structure Comparison
**Django Project:**
```python
myproject/
├── myproject/ # Project settings
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── blog/ # Django app
│ ├── models.py
│ ├── views.py
│ └── urls.py
├── shop/ # Django app
└── users/ # Django app
```
**Frappe Bench:**
```
bench/
├── apps/
│ ├── frappe/ # Core framework (comparable to Django itself)
│ ├── erpnext/ # Complete business app (like Django + DRF + Celery + admin)
│ ├── hrms/ # HR Management app
│ └── my_custom_app/ # YOUR custom app
└── sites/
└── mysite.com/ # Site instance (like Django project + database)
├── site_config.json
└── private/files/
```
### Conceptual Mapping
| Django | Frappe | Notes |
| ------------------ | ----------------- | ----------------------------------------------- |
| Model | DocType | But includes UI, permissions, API automatically |
| View | Controller method | Much less code needed |
| Admin | Desk | More powerful, auto-generated |
| DRF Serializer | Built-in | Automatic from DocType |
| Celery task | Background job | Built-in, no separate setup |
| signals | hooks.py | More structured |
| Management command | bench command | More discoverable |
### Key Architectural Differences
1. **Multi-tenancy**
- Django: One app = one database (typically)
- Frappe: One installation = many sites, each with own database
2. **Background Jobs**
- Django: Requires Celery + Redis + worker setup
- Frappe: Built-in queue system, just use `enqueue()`
3. **Real-time**
- Django: Requires Channels + Redis + ASGI setup
- Frappe: Socket.IO built-in, automatic for DocType updates
4. **Admin/Management**
- Django: Admin for models, basic CRUD
- Frappe: Full-featured Desk with reports, dashboards, permissions
5. **API**
- Django: Manual DRF setup, serializers, views
- Frappe: Automatic REST + RPC from DocType definitions
### Code Comparison Example
**Creating a "Customer" model:**
Django (requires ~50+ lines):
```python
# models.py
class Customer(models.Model):
name = models.CharField(max_length=100)
email = models.EmailField(unique=True)
# serializers.py
class CustomerSerializer(serializers.ModelSerializer):
# ...
# views.py
class CustomerViewSet(viewsets.ModelViewSet):
# ...
# urls.py
router.register(r'customers', CustomerViewSet)
# admin.py
@admin.register(Customer)
class CustomerAdmin(admin.ModelAdmin):
# ...
```
Frappe (DocType JSON + ~10 lines Python):
```json
// customer.json (auto-generated via UI or code)
{
"name": "Customer",
"fields": [
{ "fieldname": "customer_name", "fieldtype": "Data" },
{ "fieldname": "email", "fieldtype": "Data", "unique": 1 }
]
}
```
```python
# customer.py (only for custom business logic)
import frappe
from frappe.model.document import Document
class Customer(Document):
def validate(self):
# Custom validation logic only
pass
```
✅ **Automatically includes:**
- REST API (`/api/resource/Customer`)
- List view, Form view
- Search, Filters, Sorting
- Permissions (Create, Read, Update, Delete)
- Audit trail (created_by, modified_by, versions)
- Print formats, Email templates
### When to Choose Frappe vs Django
**Choose Frappe when:**
- Building business applications (ERP, CRM, project management)
- Need multi-tenancy out-of-the-box
- Want rapid development with auto-generated UI
- Need role-based permissions and workflows
- Building for non-technical users who need customization
**Choose Django when:**
- Building consumer web apps (social media, e-commerce frontend)
- Need full control over every aspect
- Have highly custom UI requirements
- Team is already Django-expert
- Building API-only services
**Hybrid Approach:**
Many teams use both: Frappe for back-office/admin tools, Django for customer-facing web apps.

View file

@ -0,0 +1,18 @@
---
title: External Links
---
# External Links
## Official Documentation
- [Frappe Framework Docs](https://frappeframework.com/docs) - Core framework documentation
- [Frappe Docker Docs](https://github.com/frappe/frappe_docker/tree/main/docs) - This repository's docs
- [ERPNext Documentation](https://docs.erpnext.com) - ERPNext user and developer docs
- [Docker Documentation](https://docs.docker.com) - Docker fundamentals
## Community Resources
- [Frappe Forum](https://discuss.frappe.io) - Community Q&A
- [Frappe School](https://frappe.school) - Video tutorials
- [Frappe GitHub](https://github.com/frappe/frappe) - Framework source code

View file

@ -0,0 +1,312 @@
---
title: GitHub Actions Image Workflows
---
This document describes the current workflow setup for shared core images and reusable downstream app images.
# Workflow roles
The current workflow layout is:
- `.github/workflows/core-build-develop.yml`
- `.github/workflows/core-build-stable.yml`
- `.github/workflows/core-build-test-images.yml`
- `.github/workflows/core-publish-images.yml`
- `.github/workflows/app-build-image.yml`
`core-build-develop.yml` and `core-build-stable.yml` are orchestration workflows.
They decide when the core image pipeline runs.
`core-build-test-images.yml` is the reusable workflow that:
- resolves the image versions for the requested release line
- builds the shared core images into a local registry
- runs the test suite against those images
`core-publish-images.yml` is the reusable workflow that:
- publishes the tested images to Docker Hub
- publishes `base` and `build` to GHCR
`app-build-image.yml` is the reusable workflow that downstream repositories call to:
- create an `apps.json` file from the caller's app repository and ref
- build `images/layered/Containerfile`
- consume existing `base` and `build` images
- install the requested app into the final image
- optionally push the final app image to the caller's registry
# Current flow
The current structure is:
```text
core orchestration
-> core build and test
-> core publish
downstream app workflow
-> consume published base and build
-> install app
-> publish final app image
```
Current Mermaid overview:
```mermaid
flowchart TD
subgraph Core["Core image flow"]
A[core-build-develop.yml or core-build-stable.yml]
B[core-build-test-images.yml]
C[Resolve versions]
D[Build local test images]
E[Run pytest]
F[core-publish-images.yml]
G[Push Docker Hub: erpnext, base, build]
H[Push GHCR: base, build]
A --> B
B --> C
C --> D
D --> E
E --> F
F --> G
F --> H
end
subgraph App["Downstream app flow"]
I[Downstream repo workflow]
J[app-build-image.yml]
K[Create apps.json]
L[Build images/layered/Containerfile]
M[Install app]
N[Push final app image]
I --> J
J --> K
K --> L
L --> M
M --> N
end
G --> J
H --> J
```
More concretely:
```text
core-build-test-images.yml
-> resolves frappe and erpnext tags
-> builds images into a local CI registry
-> runs tests
core-publish-images.yml
-> pushes Docker Hub: erpnext, base, build
-> pushes GHCR: base, build
app-build-image.yml
-> pulls:
- <prefix>/base:<frappe_ref>
- <prefix>/build:<frappe_ref>
-> installs app from app_repo + app_ref
-> pushes final image_name:image_tag
```
# Naming convention
GitHub Actions requires workflow files to stay directly inside `.github/workflows`.
Subdirectories are not supported for workflow files, so structure should come from file names and `name:` values.
Recommended file naming convention:
```text
<area>-<action>-<subject>.yml
```
Current examples:
- `core-build-bench.yml`
- `core-build-develop.yml`
- `core-build-stable.yml`
- `core-build-test-images.yml`
- `core-publish-images.yml`
- `app-build-image.yml`
- `docs-publish-site.yml`
Recommended visible workflow names:
- `Core / Build Bench`
- `Core / Build Develop`
- `Core / Build Stable`
- `Core / Build and Test Images`
- `Core / Publish Images`
- `App / Build Image`
- `Docs / Publish Site`
# Style rules
To keep workflows predictable, use one convention per category instead of mixing styles.
Workflow file names should use kebab-case:
```text
core-build-test-images.yml
app-build-image.yml
```
Workflow display names should use short title-style groups:
```text
Core / Build and Test Images
App / Build Image
```
Workflow inputs should use snake_case:
```yaml
app_name:
frappe_ref:
image_name:
```
Environment variables should use upper snake case:
```text
FRAPPE_IMAGE_PREFIX
PYTHON_VERSION
NODE_VERSION
```
The recommended rule set is:
- workflow file names: kebab-case
- workflow `name:` values: grouped title case
- workflow inputs: snake_case
- job ids and step ids: snake_case where practical
- environment variables: UPPER_SNAKE_CASE
This means `-` is preferred for file names, while `_` remains appropriate for YAML keys, inputs, and environment variables.
# Important inputs in `app-build-image.yml`
The reusable app workflow is controlled mainly by these inputs:
- `app_name`
The application directory name, for example `crm`
- `app_repo`
The Git repository to install, for example `frappe/crm`
- `app_ref`
The branch or tag to install, for example `develop`
- `frappe_ref`
The tag of the existing `base` and `build` images, for example `version-16`
- `frappe_image_prefix`
Where the shared `base` and `build` images come from, for example `frappe` or `ghcr.io/frappe`
- `image_name`
The final target image name, for example `ghcr.io/acme/crm`
- `image_tag`
The final target image tag, for example `develop`
- `registry`
The registry for the final push, for example `ghcr.io` or `docker.io`
The key distinction is:
```text
frappe_image_prefix = source of shared base/build images
image_name = destination of the final app image
```
# Example: caller repository publishes to GHCR
This example assumes:
- shared base images exist in `ghcr.io/frappe/base` and `ghcr.io/frappe/build`
- the caller repository wants to publish its own app image to `ghcr.io/acme/crm`
```yaml
name: App / Build CRM Image
on:
workflow_dispatch:
push:
branches:
- develop
permissions:
contents: read
packages: write
jobs:
build-image:
uses: frappe/frappe_docker/.github/workflows/app-build-image.yml@main
with:
app_name: crm
app_repo: acme/crm
app_ref: develop
frappe_ref: version-16
frappe_image_prefix: ghcr.io/frappe
image_name: ghcr.io/acme/crm
image_tag: develop
registry: ghcr.io
push: true
platforms: linux/amd64
```
What happens:
```text
1. app-build-image.yml is called
2. apps.json is generated from acme/crm + develop
3. the workflow builds images/layered/Containerfile
4. layered uses:
- ghcr.io/frappe/build:version-16
- ghcr.io/frappe/base:version-16
5. CRM is installed
6. the final image is pushed to ghcr.io/acme/crm:develop
```
For GHCR, the caller workflow should grant:
- `permissions: packages: write`
The reusable workflow then logs in with the workflow token.
# Example: caller repository publishes to Docker Hub
This example assumes:
- shared base images come from Docker Hub under `frappe`
- the caller repository wants to publish its app image to Docker Hub as `acme/crm`
```yaml
name: App / Build CRM Image
on:
workflow_dispatch:
push:
branches:
- develop
jobs:
build-image:
uses: frappe/frappe_docker/.github/workflows/app-build-image.yml@main
with:
app_name: crm
app_repo: acme/crm
app_ref: develop
frappe_ref: version-16
frappe_image_prefix: frappe
image_name: acme/crm
image_tag: develop
registry: docker.io
push: true
platforms: linux/amd64
secrets:
REGISTRY_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
REGISTRY_PASSWORD: ${{ secrets.DOCKERHUB_TOKEN }}
```
In this case:
- shared images are pulled from `frappe/base:version-16` and `frappe/build:version-16`
- the final image is pushed to Docker Hub as `acme/crm:develop`

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

@ -0,0 +1,3 @@
---
title: References
---

View file

@ -0,0 +1,64 @@
---
title: Custom Apps
---
# Frappe Custom Applications
## What Are Frappe Custom Apps?
Custom apps are self-contained, modular business applications that extend Frappe's functionality. They follow a convention-over-configuration approach where the framework provides most boilerplate automatically.
## Custom App Structure
```
my_custom_app/
├── hooks.py # App configuration and hooks into Frappe lifecycle
├── modules.txt # List of business modules in this app
├── my_custom_app/
│ ├── __init__.py
│ ├── config/
│ │ └── desktop.py # Desktop workspace icons and shortcuts
│ ├── my_module/ # Business domain module (e.g., sales, inventory)
│ │ ├── doctype/ # Document Types (data models)
│ │ │ ├── customer/
│ │ │ │ ├── customer.py # Python controller (business logic)
│ │ │ │ ├── customer.json # Model definition (schema, validation)
│ │ │ │ └── customer.js # Frontend logic (UI interactions)
│ │ └── page/ # Custom pages (dashboards, reports)
│ ├── public/ # Static assets (CSS, JS, images)
│ ├── templates/ # Jinja2 templates for web pages
│ └── www/ # Web pages accessible via routes
└── requirements.txt # Python package dependencies
```
## Built-in Features (Auto-generated)
Every Frappe app automatically includes:
- **REST API** - Automatic CRUD endpoints from DocType definitions
- **Permissions system** - Row-level and field-level access control
- **Audit trails** - Automatic version tracking and change history
- **Custom fields** - Runtime field additions without code changes
- **Workflows** - Configurable approval and state management
- **Reports** - Query builder and report designer
- **Print formats** - PDF generation with custom templates
- **Email integration** - Template-based email sending
- **File attachments** - Document attachment management
## Creating Custom Apps
```bash
# Enter the development container
docker exec -it <container_name> bash
# Create new app (interactive prompts will ask for details)
bench new-app my_custom_app
# Install app to a site
bench --site mysite.com install-app my_custom_app
# Create a new DocType (data model)
bench --site mysite.com console
>>> bench.new_doc("DocType", {...})
# Or use the web UI: Setup → Customize → DocType → New
```

View file

@ -0,0 +1,62 @@
---
title: Docker Bind Mounts
---
# Docker Bind Mounts
## What Are Bind Mounts?
Bind mounts create a direct connection between a directory on your host machine and a directory inside a container. Changes in either location are immediately reflected in the other - perfect for development where you want to edit code on your host and see changes in the container.
## Bind Mount vs Named Volume vs Anonymous Volume
| Type | Syntax | Use Case | Persistence |
| -------------------- | ------------------------------ | -------------------------- | ---------------------------- |
| **Bind Mount** | `./local/path:/container/path` | Development, config files | On host filesystem |
| **Named Volume** | `volume_name:/container/path` | Production data, databases | Docker-managed |
| **Anonymous Volume** | `/container/path` | Temporary/cache data | Docker-managed, auto-deleted |
## Bind Mount Examples
```yaml
services:
backend:
volumes:
# Development: Edit code on host, run in container
- ./my_custom_app:/home/frappe/frappe-bench/apps/my_custom_app
# Configuration: Override container config with host file
- ./custom-config.json:/home/frappe/frappe-bench/sites/common_site_config.json:ro # :ro = read-only
# Logs: Access container logs on host for debugging
- ./logs:/home/frappe/frappe-bench/logs
# Database (not recommended for production)
- ./data/mysql:/var/lib/mysql
# Named volume for production database
db:
volumes:
- db_data:/var/lib/mysql # Managed by Docker, survives container deletion
volumes:
db_data: # Define named volume
```
## Performance Optimization (macOS/Windows)
Docker on macOS/Windows uses a VM, making bind mounts slower. Use these flags:
```yaml
volumes:
# :cached - Host writes are buffered (good for general development)
- ./development:/home/frappe/frappe-bench:cached
# :delegated - Container writes are buffered (best when container writes heavily)
- ./development:/home/frappe/frappe-bench:delegated
# :consistent - Full synchronization (slowest but safest)
- ./development:/home/frappe/frappe-bench:consistent
```
**Recommendation:** Use `:cached` for most development work on macOS/Windows.

View file

@ -0,0 +1,3 @@
---
title: Concepts
---

View file

@ -1,3 +1,7 @@
---
title: Getting Started
---
# Getting Started with Frappe Docker
_A comprehensive guide for developers getting started with Frappe Docker, with comparisons to Django for teams familiar with that framework_
@ -85,7 +89,7 @@ Four predefined Dockerfiles are available, each serving different use cases:
- **images/layered/** - Same final contents as `custom` but based on prebuilt images from Docker Hub; faster builds for production when using Frappe-managed dependency versions
- **images/production/** - Installs only Frappe and ERPNext (not customizable with `apps.json`); best for **quick starts or exploration**; for real deployments, use `custom` or `layered`
> **Note:** For detailed build arguments and advanced configuration options, see [docs/container-setup/01-overview.md](container-setup/01-overview.md).
> **Note:** For detailed build arguments and advanced configuration options, see [docs/02-setup/01-overview.md](02-setup/01-overview.md).
### 📁 overrides/ - Compose File Extensions
@ -93,8 +97,15 @@ Docker Compose "overrides" that extend the base compose.yaml for different scena
- **compose.mariadb.yaml** - Adds MariaDB database service
- **compose.redis.yaml** - Adds Redis caching service
- **compose.proxy.yaml** - Adds Traefik reverse proxy for multi-site hosting
- **compose.https.yaml** - Adds SSL/TLS certificate management
- **compose.proxy.yaml** - Adds Traefik reverse proxy for multi-site hosting (label-based routing)
- **compose.https.yaml** - Adds Traefik HTTPS + automatic certs (uses `SITES_RULE`)
- **compose.nginxproxy.yaml** - Adds nginx-proxy reverse proxy (HTTP, env-based `VIRTUAL_HOST`)
- **compose.nginxproxy-ssl.yaml** - Adds nginx-proxy + acme-companion (HTTPS, env-based `LETSENCRYPT_HOST`)
**Proxy choice:**
- Traefik is more flexible for advanced routing and multi-bench setups
- nginx-proxy is simpler for a single bench with host-based routing.
### 📁 development/ - Dev Environment
@ -103,8 +114,8 @@ Docker Compose "overrides" that extend the base compose.yaml for different scena
### 📁 resources/ - Runtime Templates
- **nginx-entrypoint.sh** - Dynamic Nginx configuration generator script
- **nginx-template.conf** - Nginx configuration template with variable substitution
- **core/nginx/nginx-entrypoint.sh** - Dynamic Nginx configuration generator script
- **core/nginx/nginx-template.conf** - Nginx configuration template with variable substitution
## Custom Apps Explained
@ -848,11 +859,11 @@ Many teams use both: Frappe for back-office/admin tools, Django for customer-fac
### Key Files in This Repository
- [`docs/development.md`](development.md) - Detailed development setup
- [`docs/container-setup/env-variables.md`](container-setup/env-variables.md) - Environment variable reference
- [`docs/single-server-example.md`](single-server-example.md) - Production deployment guide
- [`docs/site-operations.md`](site-operations.md) - Common site management tasks
- [`development/installer.py`](../development/installer.py) - Automated setup script
- [`docs/05-development/01-development.md`](05-development/01-development.md) - Detailed development setup
- [`docs/02-setup/04-env-variables.md`](02-setup/04-env-variables.md) - Environment variable reference
- [`docs/02-setup/07-single-server-example.md`](02-setup/07-single-server-example.md) - Production deployment guide
- [`docs/04-operations/01-site-operations.md`](04-operations/01-site-operations.md) - Common site management tasks
- `development/installer.py` - Automated setup script
- [`pwd.yml`](../pwd.yml) - Quick test configuration
- [`compose.yaml`](../compose.yaml) - Base Docker Compose configuration
@ -947,7 +958,7 @@ bench update # Update framework and apps
### Getting Help
1. **Check existing docs** - Most issues covered in [`docs/troubleshoot.md`](troubleshoot.md)
1. **Check existing docs** - Most issues covered in [`docs/07-troubleshooting/01-troubleshoot.md](07-troubleshooting/01-troubleshoot.md)
2. **Search Frappe Forum** - [discuss.frappe.io](https://discuss.frappe.io)
3. **GitHub Issues** - Search existing issues first
4. **Discord/Telegram** - Community real-time chat (links in main repo)
@ -958,7 +969,7 @@ Found issues or improvements for this guide?
- Create an issue: [frappe_docker/issues](https://github.com/frappe/frappe_docker/issues)
- Submit focused PRs: keep updates scoped and split large efforts across multiple pull requests.
- Review [CONTRIBUTING.md](../CONTRIBUTING.md) for coding standards and review expectations.
- Review CONTRIBUTING.md: for coding standards and review expectations.
---

26
docs/index.md Normal file
View file

@ -0,0 +1,26 @@
---
# https://vitepress.dev/reference/default-theme-home-page
layout: home
hero:
name: "Frappe in Docker"
# text: "Frappe in Container"
tagline: "Documentation to use Docker based setup of Frappe framework"
image:
src: /frappe-docker.png
actions:
- theme: brand
text: Getting Started
link: /getting-started
- theme: alt
text: Single Compose Setup
link: /01-getting-started/04-single-compose-setup
features:
- title: Containerised
details: All the power of Frappe, but in the container
- title: Setup in Minutes
details: Get it started in a few minutes
- title: Production Ready
details: Deploy production applications with ease
---

19
docs/package.json Normal file
View file

@ -0,0 +1,19 @@
{
"devDependencies": {
"vitepress": "2.0.0-alpha.16",
"vitepress-sidebar": "1.33.1"
},
"scripts": {
"docs:dev": "vitepress dev",
"docs:build": "vitepress build",
"docs:preview": "vitepress preview"
},
"pnpm": {
"overrides": {
"vite": "7.3.2",
"minimatch": "10.2.5",
"picomatch": "4.0.4"
}
},
"packageManager": "pnpm@10.28.2+sha512.41872f037ad22f7348e3b1debbaf7e867cfd448f2726d9cf74c08f19507c31d2c8e7a11525b983febc2df640b5438dee6023ebb1f84ed43cc2d654d2bc326264"
}

1759
docs/pnpm-lock.yaml Normal file

File diff suppressed because it is too large Load diff

BIN
docs/public/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

View file

@ -1,6 +1,6 @@
# Reference: https://github.com/frappe/frappe_docker/blob/main/docs/environment-variables.md
# Reference: https://github.com/frappe/frappe_docker/blob/main/docs/02-setup/04-env-variables.md
ERPNEXT_VERSION=v15.95.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
@ -29,6 +40,9 @@ FRAPPE_SITE_NAME_HEADER=
# Default value is `8080`.
HTTP_PUBLISH_PORT=
# Default value is `443`.
HTTPS_PUBLISH_PORT=
# Default value is `127.0.0.1`. Set IP address as our trusted upstream address.
UPSTREAM_REAL_IP_ADDRESS=
@ -49,8 +63,14 @@ PROXY_READ_TIMEOUT=
# Necessary if the upload limit in the frappe application is increased
CLIENT_MAX_BODY_SIZE=
# Only with traefik overrides
# Single site: SITES_RULE=Host(`erp.example.com`)
# Multiple sites: SITES_RULE=Host(`a.example.com`) || Host(`b.example.com`)
# More https://doc.traefik.io/traefik/routing/routers/#rule
# About acme https://doc.traefik.io/traefik/https/acme/#domain-definition
SITES_RULE=Host(`erp.example.com`)
# Only with nginxproxy overrides
# Single site: NGINX_PROXY_HOSTS=erp.example.com
# Multiple sites: NGINX_PROXY_HOSTS=erp.example.com,www.erp.example.com
NGINX_PROXY_HOSTS=erp.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 \
@ -73,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 \
@ -99,18 +105,18 @@ USER frappe
WORKDIR /home/frappe
# Install Python via pyenv
ENV PYTHON_VERSION_V14=3.10.13
ENV PYTHON_VERSION_PREV=3.12.12
ENV PYTHON_VERSION=3.14.2
ENV PYENV_ROOT=/home/frappe/.pyenv
ENV PATH=$PYENV_ROOT/shims:$PYENV_ROOT/bin:$PATH
# From https://github.com/pyenv/pyenv#basic-github-checkout
RUN git clone --depth 1 https://github.com/pyenv/pyenv.git .pyenv \
&& pyenv install $PYTHON_VERSION_V14 \
&& pyenv install $PYTHON_VERSION_PREV \
&& pyenv install $PYTHON_VERSION \
&& PYENV_VERSION=$PYTHON_VERSION_V14 pip install --no-cache-dir virtualenv \
&& PYENV_VERSION=$PYTHON_VERSION_PREV pip install --no-cache-dir virtualenv \
&& PYENV_VERSION=$PYTHON_VERSION pip install --no-cache-dir virtualenv \
&& pyenv global $PYTHON_VERSION $PYTHON_VERSION_v14 \
&& pyenv global $PYTHON_VERSION $PYTHON_VERSION_PREV \
&& sed -Ei -e '/^([^#]|$)/ {a export PYENV_ROOT="/home/frappe/.pyenv" a export PATH="$PYENV_ROOT/bin:$PATH" a ' -e ':a' -e '$!{n;ba};}' ~/.profile \
&& echo 'eval "$(pyenv init --path)"' >>~/.profile \
&& echo 'eval "$(pyenv init -)"' >>~/.bashrc
@ -126,15 +132,15 @@ RUN git clone ${GIT_REPO} --depth 1 -b ${GIT_BRANCH} .bench \
&& echo "export BENCH_DEVELOPER=1" >>/home/frappe/.bashrc
# Install Node via nvm
ENV NODE_VERSION_14=16.20.2
ENV NODE_VERSION=24.12.0
ENV NODE_VERSION_PREV=22.22.0
ENV NODE_VERSION=24.13.0
ENV NVM_DIR=/home/frappe/.nvm
ENV PATH=${NVM_DIR}/versions/node/v${NODE_VERSION}/bin/:${PATH}
RUN wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh | bash \
&& . ${NVM_DIR}/nvm.sh \
&& nvm install ${NODE_VERSION_14} \
&& nvm use v${NODE_VERSION_14} \
&& nvm install ${NODE_VERSION_PREV} \
&& nvm use v${NODE_VERSION_PREV} \
&& npm install -g yarn \
&& nvm install ${NODE_VERSION} \
&& nvm use v${NODE_VERSION} \

View file

@ -1,13 +1,16 @@
ARG PYTHON_VERSION=3.11.6
ARG PYTHON_VERSION=3.14.2
ARG DEBIAN_BASE=bookworm
FROM python:${PYTHON_VERSION}-slim-${DEBIAN_BASE} AS base
COPY resources/nginx-template.conf /templates/nginx/frappe.conf.template
COPY resources/nginx-entrypoint.sh /usr/local/bin/nginx-entrypoint.sh
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
ARG WKHTMLTOPDF_VERSION=0.12.6.1-3
ARG WKHTMLTOPDF_DISTRO=bookworm
ARG NODE_VERSION=20.19.2
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}
@ -46,6 +49,7 @@ RUN useradd -ms /bin/bash frappe \
&& nvm install ${NODE_VERSION} \
&& nvm use v${NODE_VERSION} \
&& npm install -g yarn \
&& corepack enable pnpm \
&& nvm alias default v${NODE_VERSION} \
&& rm -rf ${NVM_DIR}/.cache \
&& echo 'export NVM_DIR="/home/frappe/.nvm"' >>/home/frappe/.bashrc \
@ -58,9 +62,15 @@ 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 \
&& mkdir -p /etc/nginx/snippets \
&& pip3 install frappe-bench \
# Fixes for non-root nginx and logs to stdout
&& sed -i '/user www-data/d' /etc/nginx/nginx.conf \
@ -68,12 +78,14 @@ RUN useradd -ms /bin/bash frappe \
&& touch /run/nginx.pid \
&& chown -R frappe:frappe /etc/nginx/conf.d \
&& chown -R frappe:frappe /etc/nginx/nginx.conf \
&& chown -R frappe:frappe /etc/nginx/snippets \
&& chown -R frappe:frappe /var/log/nginx \
&& chown -R frappe:frappe /var/lib/nginx \
&& chown -R frappe:frappe /run/nginx.pid \
&& chmod 755 /usr/local/bin/nginx-entrypoint.sh \
&& chmod 644 /templates/nginx/frappe.conf.template
FROM base AS builder
RUN apt-get update \
@ -107,18 +119,16 @@ RUN apt-get update \
libbz2-dev \
&& rm -rf /var/lib/apt/lists/*
# apps.json includes
ARG APPS_JSON_BASE64
RUN if [ -n "${APPS_JSON_BASE64}" ]; then \
mkdir /opt/frappe && echo "${APPS_JSON_BASE64}" | base64 -d > /opt/frappe/apps.json; \
fi
USER frappe
ARG FRAPPE_BRANCH=version-15
ARG FRAPPE_BRANCH=version-16
ARG FRAPPE_PATH=https://github.com/frappe/frappe
RUN export APP_INSTALL_ARGS="" && \
if [ -n "${APPS_JSON_BASE64}" ]; then \
ARG CACHE_BUST=""
RUN --mount=type=secret,id=apps_json,target=/opt/frappe/apps.json,uid=1000,gid=1000 \
: "${CACHE_BUST}" && \
export APP_INSTALL_ARGS="" && \
if [ -f /opt/frappe/apps.json ] && [ -s /opt/frappe/apps.json ]; then \
export APP_INSTALL_ARGS="--apps_path=/opt/frappe/apps.json"; \
fi && \
bench init ${APP_INSTALL_ARGS}\
@ -141,21 +151,24 @@ COPY --from=builder --chown=frappe:frappe /home/frappe/frappe-bench /home/frappe
WORKDIR /home/frappe/frappe-bench
# Move assets to image-layer storage
RUN cp -r /home/frappe/frappe-bench/sites/assets /home/frappe/frappe-bench/assets && \
rm -rf /home/frappe/frappe-bench/sites/assets
VOLUME [ \
"/home/frappe/frappe-bench/sites", \
"/home/frappe/frappe-bench/sites/assets", \
"/home/frappe/frappe-bench/logs" \
]
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" \
]
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 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 ["start.sh"]

View file

@ -1,21 +1,18 @@
ARG FRAPPE_BRANCH=version-15
ARG FRAPPE_BRANCH=version-16
ARG FRAPPE_IMAGE_PREFIX=frappe
FROM frappe/build:${FRAPPE_BRANCH} AS builder
FROM ${FRAPPE_IMAGE_PREFIX}/build:${FRAPPE_BRANCH} AS builder
ARG FRAPPE_BRANCH=version-15
ARG FRAPPE_BRANCH=version-16
ARG FRAPPE_PATH=https://github.com/frappe/frappe
ARG APPS_JSON_BASE64
USER root
RUN if [ -n "${APPS_JSON_BASE64}" ]; then \
mkdir /opt/frappe && echo "${APPS_JSON_BASE64}" | base64 -d > /opt/frappe/apps.json; \
fi
ARG CACHE_BUST=""
USER frappe
RUN export APP_INSTALL_ARGS="" && \
if [ -n "${APPS_JSON_BASE64}" ]; then \
RUN --mount=type=secret,id=apps_json,target=/opt/frappe/apps.json,uid=1000,gid=1000 \
: "${CACHE_BUST}" && \
export APP_INSTALL_ARGS="" && \
if [ -f /opt/frappe/apps.json ] && [ -s /opt/frappe/apps.json ]; then \
export APP_INSTALL_ARGS="--apps_path=/opt/frappe/apps.json"; \
fi && \
bench init ${APP_INSTALL_ARGS}\
@ -30,7 +27,7 @@ RUN export APP_INSTALL_ARGS="" && \
echo "{}" > sites/common_site_config.json && \
find apps -mindepth 1 -path "*/.git" | xargs rm -fr
FROM frappe/base:${FRAPPE_BRANCH} AS backend
FROM ${FRAPPE_IMAGE_PREFIX}/base:${FRAPPE_BRANCH} AS backend
USER frappe
@ -38,21 +35,24 @@ COPY --from=builder --chown=frappe:frappe /home/frappe/frappe-bench /home/frappe
WORKDIR /home/frappe/frappe-bench
# Move assets to image-layer storage
RUN cp -r /home/frappe/frappe-bench/sites/assets /home/frappe/frappe-bench/assets && \
rm -rf /home/frappe/frappe-bench/sites/assets
VOLUME [ \
"/home/frappe/frappe-bench/sites", \
"/home/frappe/frappe-bench/sites/assets", \
"/home/frappe/frappe-bench/logs" \
]
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" \
]
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 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 ["start.sh"]

View file

@ -1,10 +1,12 @@
ARG PYTHON_VERSION=3.11.6
ARG PYTHON_VERSION=3.14.2
ARG DEBIAN_BASE=bookworm
FROM python:${PYTHON_VERSION}-slim-${DEBIAN_BASE} AS base
ARG WKHTMLTOPDF_VERSION=0.12.6.1-3
ARG WKHTMLTOPDF_DISTRO=bookworm
ARG NODE_VERSION=20.19.2
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}
@ -43,6 +45,7 @@ RUN useradd -ms /bin/bash frappe \
&& nvm install ${NODE_VERSION} \
&& nvm use v${NODE_VERSION} \
&& npm install -g yarn \
&& corepack enable pnpm \
&& nvm alias default v${NODE_VERSION} \
&& rm -rf ${NVM_DIR}/.cache \
&& echo 'export NVM_DIR="/home/frappe/.nvm"' >>/home/frappe/.bashrc \
@ -55,9 +58,15 @@ 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 \
&& mkdir -p /etc/nginx/snippets \
&& pip3 install frappe-bench \
# Fixes for non-root nginx and logs to stdout
&& sed -i '/user www-data/d' /etc/nginx/nginx.conf \
@ -65,12 +74,15 @@ RUN useradd -ms /bin/bash frappe \
&& touch /run/nginx.pid \
&& chown -R frappe:frappe /etc/nginx/conf.d \
&& chown -R frappe:frappe /etc/nginx/nginx.conf \
&& chown -R frappe:frappe /etc/nginx/snippets \
&& chown -R frappe:frappe /var/log/nginx \
&& chown -R frappe:frappe /var/lib/nginx \
&& chown -R frappe:frappe /run/nginx.pid
COPY resources/nginx-template.conf /templates/nginx/frappe.conf.template
COPY resources/nginx-entrypoint.sh /usr/local/bin/nginx-entrypoint.sh
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
@ -103,10 +115,10 @@ USER frappe
FROM build AS builder
ARG FRAPPE_BRANCH=version-15
ARG FRAPPE_BRANCH=version-16
ARG FRAPPE_PATH=https://github.com/frappe/frappe
ARG ERPNEXT_REPO=https://github.com/frappe/erpnext
ARG ERPNEXT_BRANCH=version-15
ARG ERPNEXT_BRANCH=version-16
RUN bench init \
--frappe-branch=${FRAPPE_BRANCH} \
--frappe-path=${FRAPPE_PATH} \
@ -130,21 +142,24 @@ COPY --from=builder --chown=frappe:frappe /home/frappe/frappe-bench /home/frappe
WORKDIR /home/frappe/frappe-bench
# Move assets to image-layer storage
RUN cp -r /home/frappe/frappe-bench/sites/assets /home/frappe/frappe-bench/assets && \
rm -rf /home/frappe/frappe-bench/sites/assets
VOLUME [ \
"/home/frappe/frappe-bench/sites", \
"/home/frappe/frappe-bench/sites/assets", \
"/home/frappe/frappe-bench/logs" \
]
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" \
]
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 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 ["start.sh"]

View file

@ -13,7 +13,6 @@ services:
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_unicode_ci
- --skip-character-set-client-handshake
- --skip-innodb-read-only-compressed
environment:
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD:-changeit}
MARIADB_AUTO_UPGRADE: 1

View file

@ -20,7 +20,6 @@ services:
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_unicode_ci
- --skip-character-set-client-handshake
- --skip-innodb-read-only-compressed # Temporary fix for MariaDB 10.6
environment:
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD:-123}
MARIADB_AUTO_UPGRADE: 1

View file

@ -0,0 +1,44 @@
# Provides a service for automated migration of a given site.
# Compose extension fields of base compose.yaml. See https://github.com/frappe/frappe_docker/blob/main/compose.yaml
# Needed for merging compose files.
x-customizable-image: &customizable_image
# By default the image used only contains the `frappe` and `erpnext` apps.
# See https://github.com/frappe/frappe_docker/blob/main/docs/02-setup/02-build-setup.md#define-custom-apps
# about using custom images.
image: ${CUSTOM_IMAGE:-frappe/erpnext}:${CUSTOM_TAG:-$ERPNEXT_VERSION}
pull_policy: ${PULL_POLICY:-always}
restart: ${RESTART_POLICY:-unless-stopped}
x-depends-on-configurator: &depends_on_configurator
depends_on:
configurator:
condition: service_completed_successfully
x-backend-defaults: &backend_defaults
<<: [*depends_on_configurator, *customizable_image]
volumes:
- sites:/home/frappe/frappe-bench/sites
services:
migrator:
<<: *backend_defaults
platform: linux/amd64
entrypoint:
- bash
- -c
command:
- >
if [ "$$MIGRATE_SITES" != "true" ]; then
echo "[migrator] Migration disabled";
exit 0;
fi;
if [ -z "$$(find sites -mindepth 2 -maxdepth 2 -name site_config.json 2>/dev/null)" ]; then
echo "[migrator] No sites found, skipping migration";
exit 0;
fi;
echo "[migrator] Migrating all sites";
bench --site all migrate;
environment:
MIGRATE_SITES: ${MIGRATE_SITES:-true}
restart: on-failure:5

View file

@ -37,12 +37,10 @@ services:
redis-cache:
networks:
- bench-network
- mariadb-network
redis-queue:
networks:
- bench-network
- mariadb-network
networks:
traefik-public:

View file

@ -0,0 +1,28 @@
services:
frontend:
environment:
LETSENCRYPT_HOST: ${NGINX_PROXY_HOSTS:?No NGINX_PROXY_HOSTS set}
nginx-proxy:
labels:
com.github.nginx-proxy.nginx: "true"
ports:
- ${HTTP_PUBLISH_PORT:-80}:80
- ${HTTPS_PUBLISH_PORT:-443}:443
acme-companion:
image: nginxproxy/acme-companion:latest
restart: unless-stopped
environment:
DEFAULT_EMAIL: ${LETSENCRYPT_EMAIL:?No LETSENCRYPT_EMAIL set}
volumes:
- nginx-proxy-certs:/etc/nginx/certs
- nginx-proxy-html:/usr/share/nginx/html
- nginx-proxy-vhost:/etc/nginx/vhost.d
- acme-data:/etc/acme.sh
- /var/run/docker.sock:/var/run/docker.sock:ro
depends_on:
- nginx-proxy
volumes:
acme-data:

View file

@ -0,0 +1,21 @@
services:
frontend:
environment:
VIRTUAL_HOST: ${NGINX_PROXY_HOSTS:?No NGINX_PROXY_HOSTS set}
VIRTUAL_PORT: 8080
nginx-proxy:
image: nginxproxy/nginx-proxy:alpine
restart: unless-stopped
ports:
- ${HTTP_PUBLISH_PORT:-80}:80
volumes:
- nginx-proxy-certs:/etc/nginx/certs
- nginx-proxy-html:/usr/share/nginx/html
- nginx-proxy-vhost:/etc/nginx/vhost.d
- /var/run/docker.sock:/tmp/docker.sock:ro
volumes:
nginx-proxy-certs:
nginx-proxy-html:
nginx-proxy-vhost:

View file

@ -2,15 +2,20 @@ services:
configurator:
environment:
DB_HOST: db
DB_PORT: 5432
DB_PORT: "5432"
depends_on:
- db
db:
condition: service_healthy
db:
image: postgres:13.5
image: postgres:15.17
command: []
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
retries: 5
environment:
POSTGRES_PASSWORD: ${DB_PASSWORD:?No db password set}
POSTGRES_PASSWORD: ${DB_PASSWORD:-123}
volumes:
- db-data:/var/lib/postgresql/data

View file

@ -8,11 +8,11 @@ services:
- redis-queue
redis-cache:
image: redis:6.2-alpine
image: redis:8.6-alpine
restart: unless-stopped
redis-queue:
image: redis:6.2-alpine
image: redis:8.6-alpine
restart: unless-stopped
volumes:
- redis-queue-data:/data

27
pwd.yml
View file

@ -1,6 +1,6 @@
services:
backend:
image: frappe/erpnext:v15.95.0
image: frappe/erpnext:v16.23.0
networks:
- frappe_network
deploy:
@ -16,7 +16,7 @@ services:
MARIADB_ROOT_PASSWORD: admin
configurator:
image: frappe/erpnext:v15.95.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:v15.95.0
image: frappe/erpnext:v16.23.0
networks:
- frappe_network
deploy:
@ -78,13 +78,15 @@ services:
bench new-site --mariadb-user-host-login-scope='%' --admin-password=admin --db-root-username=root --db-root-password=admin --install-app erpnext --set-default frontend;
db:
image: mariadb:10.6
image: mariadb:11.8
networks:
- frappe_network
healthcheck:
test: mysqladmin ping -h localhost --password=admin
interval: 1s
retries: 20
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
start_period: 5s
interval: 5s
timeout: 5s
retries: 5
deploy:
restart_policy:
condition: on-failure
@ -92,7 +94,6 @@ services:
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_unicode_ci
- --skip-character-set-client-handshake
- --skip-innodb-read-only-compressed # Temporary fix for MariaDB 10.6
environment:
MYSQL_ROOT_PASSWORD: admin
MARIADB_ROOT_PASSWORD: admin
@ -100,7 +101,7 @@ services:
- db-data:/var/lib/mysql
frontend:
image: frappe/erpnext:v15.95.0
image: frappe/erpnext:v16.23.0
networks:
- frappe_network
depends_on:
@ -126,7 +127,7 @@ services:
- "8080:8080"
queue-long:
image: frappe/erpnext:v15.95.0
image: frappe/erpnext:v16.23.0
networks:
- frappe_network
deploy:
@ -145,7 +146,7 @@ services:
FRAPPE_REDIS_QUEUE: redis://redis-queue:6379
queue-short:
image: frappe/erpnext:v15.95.0
image: frappe/erpnext:v16.23.0
networks:
- frappe_network
deploy:
@ -182,7 +183,7 @@ services:
condition: on-failure
scheduler:
image: frappe/erpnext:v15.95.0
image: frappe/erpnext:v16.23.0
networks:
- frappe_network
deploy:
@ -196,7 +197,7 @@ services:
- logs:/home/frappe/frappe-bench/logs
websocket:
image: frappe/erpnext:v15.95.0
image: frappe/erpnext:v16.23.0
networks:
- frappe_network
deploy:

View file

@ -1 +1 @@
pytest==9.0.2
pytest==9.1.0

View file

@ -0,0 +1,12 @@
#!/bin/bash
set -e
ASSETS_PATH="/home/frappe/frappe-bench/sites/assets"
BAKED_PATH="/home/frappe/frappe-bench/assets"
echo "Linking fresh assets to volume..."
rm -rf "$ASSETS_PATH"
mkdir -p "$(dirname "$ASSETS_PATH")"
ln -s "$BAKED_PATH" "$ASSETS_PATH"
exec "$@"

View file

@ -6,20 +6,23 @@ upstream socketio-server {
server ${SOCKETIO} fail_timeout=0;
}
# Parse the X-Forwarded-Proto header - if set - defaulting to $scheme.
map $http_x_forwarded_proto $proxy_x_forwarded_proto {
default $scheme;
https https;
}
server {
listen 8080;
server_name ${FRAPPE_SITE_NAME_HEADER};
absolute_redirect off;
root /home/frappe/frappe-bench/sites;
proxy_buffer_size 128k;
proxy_buffers 4 256k;
proxy_busy_buffers_size 256k;
add_header X-Frame-Options "SAMEORIGIN";
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload";
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
add_header Referrer-Policy "same-origin, strict-origin-when-cross-origin";
include /etc/nginx/snippets/security_headers.conf;
set_real_ip_from ${UPSTREAM_REAL_IP_ADDRESS};
real_ip_header ${UPSTREAM_REAL_IP_HEADER};
@ -36,10 +39,12 @@ server {
location /socket.io {
proxy_http_version 1.1;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header X-Frappe-Site-Name ${FRAPPE_SITE_NAME_HEADER};
proxy_set_header Origin $scheme://$http_host;
proxy_set_header Origin $proxy_x_forwarded_proto://${FRAPPE_SITE_NAME_HEADER};
proxy_set_header Host $host;
proxy_pass http://socketio-server;
@ -51,6 +56,7 @@ server {
rewrite ^(.+)\.html$ $1 permanent;
location ~ ^/files/.*.(htm|html|svg|xml) {
include /etc/nginx/snippets/security_headers.conf;
add_header Content-disposition "attachment";
try_files /${FRAPPE_SITE_NAME_HEADER}/public/$uri @webserver;
}
@ -61,7 +67,7 @@ server {
location @webserver {
proxy_http_version 1.1;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
proxy_set_header X-Frappe-Site-Name ${FRAPPE_SITE_NAME_HEADER};
proxy_set_header Host $host;
proxy_set_header X-Use-X-Accel-Redirect True;

View file

@ -0,0 +1,5 @@
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
add_header X-Content-Type-Options nosniff always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "same-origin, strict-origin-when-cross-origin" always;

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

View file

@ -5,7 +5,7 @@ from typing import Any
import pytest
from tests.conftest import S3ServiceResult
from tests.utils import Compose, check_url_content
from tests.utils import Compose, check_url_content, wait_for_url
BACKEND_SERVICES = (
"backend",
@ -81,6 +81,30 @@ def test_files_reachable(frappe_site: str, tmp_path: Path, compose: Compose):
)
def test_files_html_security_headers(
frappe_site: str, tmp_path: Path, compose: Compose
):
file_path = tmp_path / "testfile.html"
file_path.write_text(
"<html><body>This is a Frappe Docker test html file</body></html>"
)
compose(
"cp",
str(file_path),
f"backend:/home/frappe/frappe-bench/sites/{frappe_site}/public/files/",
)
response = wait_for_url(
url=f"http://127.0.0.1/files/{file_path.name}",
site_name=frappe_site,
)
assert response.headers["Content-Disposition"] == "attachment"
assert response.headers["X-Frame-Options"] == "SAMEORIGIN"
assert response.headers["X-Content-Type-Options"] == "nosniff"
@pytest.mark.parametrize("service", BACKEND_SERVICES)
@pytest.mark.usefixtures("frappe_site")
def test_frappe_connections_in_backends(

View file

@ -4,6 +4,7 @@ import subprocess
import sys
import time
from contextlib import suppress
from http.client import HTTPResponse
from typing import Callable, Optional
from urllib.error import HTTPError, URLError
from urllib.request import Request, urlopen
@ -59,24 +60,11 @@ class Compose:
def check_url_content(
url: str, callback: Callable[[str], Optional[str]], site_name: str
):
request = Request(url, headers={"Host": site_name})
# This is needed to check https override
ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
for _ in range(100):
try:
response = urlopen(request, context=ctx)
except HTTPError as exc:
if exc.code not in (404, 502):
raise
except URLError:
response = wait_for_url(url=url, site_name=site_name, attempts=1)
except RuntimeError:
pass
else:
text: str = response.read().decode()
ret = callback(text)
@ -86,4 +74,26 @@ def check_url_content(
time.sleep(0.1)
raise RuntimeError(f"Couldn't verify expected content from {url}")
def wait_for_url(url: str, site_name: str, attempts: int = 100) -> HTTPResponse:
request = Request(url, headers={"Host": site_name})
# This is needed to check https override
ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
for _ in range(attempts):
try:
return urlopen(request, context=ctx)
except HTTPError as exc:
if exc.code not in (404, 502):
raise
except URLError:
pass
time.sleep(0.1)
raise RuntimeError(f"Couldn't ping {url}")