Compare commits

..

No commits in common. "main" and "v2.2.0" have entirely different histories.
main ... v2.2.0

50 changed files with 428 additions and 1800 deletions

View file

@ -49,13 +49,6 @@ 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}))
@ -77,9 +70,6 @@ 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

View file

@ -1,189 +0,0 @@
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,4 +1,4 @@
name: Core / Build Bench
name: Bench
on:
pull_request:
@ -7,7 +7,7 @@ on:
paths:
- images/bench/**
- docker-bake.hcl
- .github/workflows/core-build-bench.yml
- .github/workflows/build_bench.yml
schedule:
# Every day at 12:00 pm
@ -38,7 +38,7 @@ jobs:
run: echo "LATEST_BENCH_RELEASE=$(curl -s 'https://api.github.com/repos/frappe/bench/releases/latest' | jq -r '.tag_name')" >> "$GITHUB_ENV"
- name: Build and test
uses: docker/bake-action@v7.2.0
uses: docker/bake-action@v7.0.0
with:
source: .
targets: bench-test
@ -52,7 +52,7 @@ jobs:
- name: Push
if: ${{ github.repository == 'frappe/frappe_docker' && github.event_name != 'pull_request' }}
uses: docker/bake-action@v7.2.0
uses: docker/bake-action@v7.0.0
with:
targets: bench
push: true

View file

@ -1,12 +1,33 @@
name: Legacy / Build Develop
name: Develop build
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:
delegate:
uses: ./.github/workflows/core-build-develop.yml
permissions:
contents: read
packages: write
secrets: inherit
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 }}

View file

@ -1,12 +1,116 @@
name: Legacy / Build Stable
name: Stable build
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:
delegate:
uses: ./.github/workflows/core-build-stable.yml
permissions:
contents: write
packages: write
secrets: inherit
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: v16
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
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,51 +0,0 @@
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 }}

View file

@ -1,148 +0,0 @@
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,92 +0,0 @@
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 }}

View file

@ -1,4 +1,4 @@
name: Core / Build and Test Images
name: Build
on:
workflow_call:
@ -11,6 +11,9 @@ 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
@ -19,36 +22,16 @@ on:
required: true
type: string
description: NodeJS Version
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
secrets:
DOCKERHUB_USERNAME:
required: true
DOCKERHUB_TOKEN:
required: true
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
@ -74,12 +57,8 @@ jobs:
driver-opts: network=host
platforms: linux/${{ matrix.arch }}
- 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: Get latest versions
run: python3 ./.github/scripts/get_latest_tags.py --repo ${{ inputs.repo }} --version ${{ inputs.version }}
- name: Set build args
run: |
@ -87,7 +66,7 @@ jobs:
echo "NODE_VERSION=${{ inputs.node_version }}" >> "$GITHUB_ENV"
- name: Build
uses: docker/bake-action@v7.2.0
uses: docker/bake-action@v7.0.0
with:
source: .
push: true
@ -106,3 +85,17 @@ jobs:
- name: Test
run: venv/bin/pytest --color=yes
- name: Login
if: ${{ inputs.push }}
uses: docker/login-action@v4
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Push
if: ${{ inputs.push }}
uses: docker/bake-action@v7.0.0
with:
push: true
set: "*.platform=linux/amd64,linux/arm64"

View file

@ -1,11 +1,11 @@
name: Docs / Publish Site
name: Deploy Frappe Docker Docs to GitHub Pages
on:
push:
branches: [main]
paths:
- "docs/**"
- ".github/workflows/docs-publish-site.yml"
- ".github/workflows/publish_docs.yml"
workflow_dispatch:
permissions:
@ -32,23 +32,17 @@ jobs:
- name: Checkout
uses: actions/checkout@v6
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 10
- 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
cache: "pnpm"
cache-dependency-path: ./docs
- name: Install dependencies
run: pnpm i --frozen-lockfile
@ -57,10 +51,10 @@ jobs:
run: pnpm docs:build
- name: Upload artifact
uses: actions/upload-pages-artifact@v5
uses: actions/upload-pages-artifact@v4
with:
path: docs/.vitepress/dist
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v5
uses: actions/deploy-pages@v4

View file

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

View file

@ -143,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/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`.
- `.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`.

View file

@ -1,25 +0,0 @@
# 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,37 +1,17 @@
<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>
# 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.
## What is this?
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
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.
## Repository Structure
```bash
```
frappe_docker/
├── docs/ # Complete documentation
├── overrides/ # Docker Compose configurations for different scenarios
@ -54,18 +34,11 @@ frappe_docker/
## Documentation
The full `frappe_docker` documentation is available in [`docs/`](docs/) and published at [frappe.github.io/frappe_docker](https://frappe.github.io/frappe_docker/).
**The official documentation for `frappe_docker` is maintained in the `docs/` folder in this repository.**
### Recommended entry points:
**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.
- **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)
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.
## Prerequisites
@ -77,13 +50,17 @@ The full `frappe_docker` documentation is available in [`docs/`](docs/) and publ
## Demo setup
The fastest way to try Frappe locally is with the single-file demo setup in `pwd.yml`.
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>
### Try on your environment
> **⚠️ Disposable demo only**
>
> **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.
> **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.
First clone the repo:
@ -100,6 +77,22 @@ 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/02-setup/02-build-setup.md#define-custom-apps
# See https://github.com/frappe/frappe_docker/blob/main/docs/container-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}
@ -46,10 +46,6 @@ 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,9 +10,7 @@
"grapecity.gc-excelviewer",
"mtxr.sqltools",
"mtxr.sqltools-driver-mysql",
"vue.volar",
"esbenp.prettier-vscode",
"charliermarsh.ruff"
"visualstudioexptteam.vscodeintellicode"
],
"settings": {
"terminal.integrated.profiles.linux": {
@ -46,7 +44,6 @@
"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,6 +5,7 @@ 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-16"
"branch": "version-15"
}
]

View file

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

View file

@ -1,20 +1,3 @@
{
"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
}
"python.defaultInterpreterPath": "${workspaceFolder}/frappe-bench/env/bin/python"
}

View file

@ -62,10 +62,6 @@ group "default" {
targets = ["erpnext", "base", "build"]
}
group "base-images" {
targets = ["base", "build"]
}
function "tag" {
params = [repo, version]
result = [

View file

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

View file

@ -29,8 +29,8 @@ here is the example pwd.yml file:
```yml
services:
backend:
image: frappe/erpnext:v16.19.1
platform: linux/arm64
image: frappe/erpnext:v15
platform: linux/amd64
deploy:
restart_policy:
condition: on-failure
@ -39,8 +39,8 @@ services:
- logs:/home/frappe/frappe-bench/logs
configurator:
image: frappe/erpnext:v16.19.1
platform: linux/arm64
image: frappe/erpnext:v15
platform: linux/amd64
deploy:
restart_policy:
condition: none
@ -68,8 +68,8 @@ services:
- logs:/home/frappe/frappe-bench/logs
create-site:
image: frappe/erpnext:v16.19.1
platform: linux/arm64
image: frappe/erpnext:v15
platform: linux/amd64
deploy:
restart_policy:
condition: none
@ -100,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:11.8
platform: linux/arm64
image: mariadb:10.6
platform: linux/amd64
healthcheck:
test: mysqladmin ping -h localhost --password=admin
interval: 1s
@ -113,14 +113,15 @@ 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:v16.19.1
platform: linux/arm64
image: frappe/erpnext:v15
platform: linux/amd64
depends_on:
- websocket
deploy:
@ -144,8 +145,8 @@ services:
- "8080:8080"
queue-long:
image: frappe/erpnext:v16.19.1
platform: linux/arm64
image: frappe/erpnext:v15
platform: linux/amd64
deploy:
restart_policy:
condition: on-failure
@ -159,8 +160,8 @@ services:
- logs:/home/frappe/frappe-bench/logs
queue-short:
image: frappe/erpnext:v16.19.1
platform: linux/arm64
image: frappe/erpnext:v15
platform: linux/amd64
deploy:
restart_policy:
condition: on-failure
@ -175,7 +176,7 @@ services:
redis-queue:
image: redis:6.2-alpine
platform: linux/arm64
platform: linux/amd64
deploy:
restart_policy:
condition: on-failure
@ -184,14 +185,14 @@ services:
redis-cache:
image: redis:6.2-alpine
platform: linux/arm64
platform: linux/amd64
deploy:
restart_policy:
condition: on-failure
scheduler:
image: frappe/erpnext:v16.19.1
platform: linux/arm64
image: frappe/erpnext:v15
platform: linux/amd64
deploy:
restart_policy:
condition: on-failure
@ -203,8 +204,8 @@ services:
- logs:/home/frappe/frappe-bench/logs
websocket:
image: frappe/erpnext:v16.19.1
platform: linux/arm64
image: frappe/erpnext:v15
platform: linux/amd64
deploy:
restart_policy:
condition: on-failure

View file

@ -7,13 +7,11 @@ This guide walks you through building Frappe images from the repository resource
# Prerequisites
- git
- docker (Engine **v23.0+** with buildx) or podman
- docker 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
@ -23,7 +21,7 @@ cd frappe_docker
# Define custom apps
If you don't want to include custom apps in the image, skip this section.
If you dont want to install specific apps to the image skip this section.
To include custom apps in your image, create an `apps.json` file in the repository root:
@ -31,11 +29,11 @@ To include custom apps in your image, create an `apps.json` file in the reposito
[
{
"url": "https://github.com/frappe/erpnext",
"branch": "version-16"
"branch": "version-15"
},
{
"url": "https://github.com/frappe/hrms",
"branch": "version-16"
"branch": "version-15"
},
{
"url": "https://github.com/frappe/helpdesk",
@ -44,23 +42,24 @@ To include custom apps in your image, create an `apps.json` file in the reposito
]
```
# Build custom images
Then generate a base64-encoded string from this file:
## Manually
```bash
export APPS_JSON_BASE64=$(base64 -w 0 apps.json)
```
# Build the image
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-16 \
--secret=id=apps_json,src=apps.json \
--tag=custom:16 \
--build-arg=FRAPPE_BRANCH=version-15 \
--build-arg=APPS_JSON_BASE64=$APPS_JSON_BASE64 \
--tag=custom:15 \
--file=images/layered/Containerfile .
```
@ -68,42 +67,31 @@ docker build \
```bash
podman build \
--no-cache \
--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 \
--build-arg=FRAPPE_BRANCH=version-15 \
--build-arg=APPS_JSON_BASE64=$APPS_JSON_BASE64 \
--tag=custom:15 \
--file=images/layered/Containerfile .
```
## Automated
## Build args
This repository is fully suited for automated builds, i.e. using CI/CD pipelines.
| 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` |
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
# env file
The compose file requires several environment variables. You can either export them on your system or create a `.env` file.
@ -117,7 +105,7 @@ For this setup, make sure **at least** the following values are added to `custom
```txt
CUSTOM_IMAGE=custom
CUSTOM_TAG=16
CUSTOM_TAG=15
PULL_POLICY=missing
```
@ -127,7 +115,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

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

@ -24,10 +24,8 @@ docker compose -f compose.yaml -f overrides/compose.mariadb.yaml -f overrides/co
| 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.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 | | |

View file

@ -15,7 +15,7 @@ We will setup the following:
## 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 server that can run Docker (recommended: 2 vCPU, 4 GB RAM, 50 GB SSD).
- A public domain with DNS control.
- Two subdomains pointing to your server IP (A/AAAA records):
- `erp.your-domain.com`
@ -84,17 +84,25 @@ cat > ~/gitops/apps.json <<'EOF'
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:
Generate the BASE64 value and build:
```shell
export APPS_JSON_BASE64=$(base64 -w 0 ~/gitops/apps.json)
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 \
--build-arg=APPS_JSON_BASE64=$APPS_JSON_BASE64 \
--tag=my-erpnext-prod-image:16.0.0 \
--file=images/layered/Containerfile .
```
If `base64 -w 0` is not available on your system, use:
```shell
export APPS_JSON_BASE64=$(base64 ~/gitops/apps.json | tr -d '\n')
```
### Configure environment
Create an environment file for the bench:
@ -149,7 +157,7 @@ docker compose --project-name erpnext exec backend \
# crm.your-domain.com
docker compose --project-name erpnext exec backend \
bench new-site --mariadb-user-host-login-scope=% --db-root-password changeit --install-app crm --admin-password changeit crm.your-domain.com
bench new-site --mariadb-user-host-login-scope=% --db-root-password changeit --install-app erpnext --admin-password changeit crm.your-domain.com
```
### Notes

View file

@ -1,147 +0,0 @@
---
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

@ -1,254 +0,0 @@
---
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

@ -114,14 +114,3 @@ 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,7 +1,3 @@
---
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`.

View file

@ -1,312 +0,0 @@
---
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

@ -1,62 +0,0 @@
---
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

@ -14,7 +14,7 @@ hero:
link: /getting-started
- theme: alt
text: Single Compose Setup
link: /01-getting-started/04-single-compose-setup
link: /01-getting-started/02-single-compose-setup
features:
- title: Containerised

View file

@ -8,12 +8,5 @@
"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"
}

View file

@ -4,18 +4,13 @@ settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
overrides:
vite: 7.3.2
minimatch: 10.2.5
picomatch: 4.0.4
importers:
.:
devDependencies:
vitepress:
specifier: 2.0.0-alpha.16
version: 2.0.0-alpha.16(postcss@8.5.15)
version: 2.0.0-alpha.16(postcss@8.5.8)
vitepress-sidebar:
specifier: 1.33.1
version: 1.33.1
@ -210,6 +205,14 @@ packages:
'@iconify/types@2.0.0':
resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==}
'@isaacs/balanced-match@4.0.1':
resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==}
engines: {node: 20 || >=22}
'@isaacs/brace-expansion@5.0.0':
resolution: {integrity: sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==}
engines: {node: 20 || >=22}
'@isaacs/cliui@8.0.2':
resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
engines: {node: '>=12'}
@ -408,13 +411,12 @@ packages:
'@ungap/structured-clone@1.3.0':
resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==}
deprecated: Potential CWE-502 - Update to 1.3.1 or higher
'@vitejs/plugin-vue@6.0.5':
resolution: {integrity: sha512-bL3AxKuQySfk1iGcBsQnoRVexTPJq0Z/ixFVM8OhVJAP6ZXXXLtM7NFKWhLl30Kg7uTBqIaPXbh+nuQCuBDedg==}
engines: {node: ^20.19.0 || >=22.12.0}
peerDependencies:
vite: 7.3.2
vite: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0
vue: ^3.2.25
'@vue/compiler-core@3.5.30':
@ -529,17 +531,9 @@ packages:
argparse@1.0.10:
resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==}
balanced-match@4.0.4:
resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==}
engines: {node: 18 || 20 || >=22}
birpc@2.9.0:
resolution: {integrity: sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==}
brace-expansion@5.0.5:
resolution: {integrity: sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==}
engines: {node: 18 || 20 || >=22}
ccount@2.0.1:
resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==}
@ -607,7 +601,7 @@ packages:
resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}
engines: {node: '>=12.0.0'}
peerDependencies:
picomatch: 4.0.4
picomatch: ^3 || ^4
peerDependenciesMeta:
picomatch:
optional: true
@ -697,9 +691,9 @@ packages:
micromark-util-types@2.0.2:
resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==}
minimatch@10.2.5:
resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==}
engines: {node: 18 || 20 || >=22}
minimatch@10.1.1:
resolution: {integrity: sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==}
engines: {node: 20 || >=22}
minipass@7.1.2:
resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
@ -708,8 +702,8 @@ packages:
minisearch@7.2.0:
resolution: {integrity: sha512-dqT2XBYUOZOiC5t2HRnwADjhNS2cecp9u+TJRiJ1Qp/f5qjkeT5APcGPjHw+bz89Ms8Jp+cG4AlE+QZ/QnDglg==}
nanoid@3.3.12:
resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==}
nanoid@3.3.11:
resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
hasBin: true
@ -736,12 +730,12 @@ packages:
picocolors@1.1.1:
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
picomatch@4.0.4:
resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==}
picomatch@4.0.3:
resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==}
engines: {node: '>=12'}
postcss@8.5.15:
resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==}
postcss@8.5.8:
resolution: {integrity: sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==}
engines: {node: ^10 || ^12 || >=14}
property-information@7.1.0:
@ -848,8 +842,8 @@ packages:
vfile@6.0.3:
resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==}
vite@7.3.2:
resolution: {integrity: sha512-Bby3NOsna2jsjfLVOHKes8sGwgl4TT0E6vvpYgnAYDIF/tie7MRaFthmKuHx1NSXjiTueXH3do80FMQgvEktRg==}
vite@7.3.1:
resolution: {integrity: sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==}
engines: {node: ^20.19.0 || >=22.12.0}
hasBin: true
peerDependencies:
@ -1036,6 +1030,12 @@ snapshots:
'@iconify/types@2.0.0': {}
'@isaacs/balanced-match@4.0.1': {}
'@isaacs/brace-expansion@5.0.0':
dependencies:
'@isaacs/balanced-match': 4.0.1
'@isaacs/cliui@8.0.2':
dependencies:
string-width: 5.1.2
@ -1187,10 +1187,10 @@ snapshots:
'@ungap/structured-clone@1.3.0': {}
'@vitejs/plugin-vue@6.0.5(vite@7.3.2)(vue@3.5.30)':
'@vitejs/plugin-vue@6.0.5(vite@7.3.1)(vue@3.5.30)':
dependencies:
'@rolldown/pluginutils': 1.0.0-rc.2
vite: 7.3.2
vite: 7.3.1
vue: 3.5.30
'@vue/compiler-core@3.5.30':
@ -1215,7 +1215,7 @@ snapshots:
'@vue/shared': 3.5.30
estree-walker: 2.0.2
magic-string: 0.30.21
postcss: 8.5.15
postcss: 8.5.8
source-map-js: 1.2.1
'@vue/compiler-ssr@3.5.30':
@ -1295,14 +1295,8 @@ snapshots:
dependencies:
sprintf-js: 1.0.3
balanced-match@4.0.4: {}
birpc@2.9.0: {}
brace-expansion@5.0.5:
dependencies:
balanced-match: 4.0.4
ccount@2.0.1: {}
character-entities-html4@2.1.0: {}
@ -1376,9 +1370,9 @@ snapshots:
dependencies:
is-extendable: 0.1.1
fdir@6.5.0(picomatch@4.0.4):
fdir@6.5.0(picomatch@4.0.3):
optionalDependencies:
picomatch: 4.0.4
picomatch: 4.0.3
focus-trap@7.8.0:
dependencies:
@ -1396,7 +1390,7 @@ snapshots:
dependencies:
foreground-child: 3.3.1
jackspeak: 4.1.1
minimatch: 10.2.5
minimatch: 10.1.1
minipass: 7.1.2
package-json-from-dist: 1.0.1
path-scurry: 2.0.1
@ -1484,15 +1478,15 @@ snapshots:
micromark-util-types@2.0.2: {}
minimatch@10.2.5:
minimatch@10.1.1:
dependencies:
brace-expansion: 5.0.5
'@isaacs/brace-expansion': 5.0.0
minipass@7.1.2: {}
minisearch@7.2.0: {}
nanoid@3.3.12: {}
nanoid@3.3.11: {}
oniguruma-parser@0.12.1: {}
@ -1515,11 +1509,11 @@ snapshots:
picocolors@1.1.1: {}
picomatch@4.0.4: {}
picomatch@4.0.3: {}
postcss@8.5.15:
postcss@8.5.8:
dependencies:
nanoid: 3.3.12
nanoid: 3.3.11
picocolors: 1.1.1
source-map-js: 1.2.1
@ -1629,8 +1623,8 @@ snapshots:
tinyglobby@0.2.15:
dependencies:
fdir: 6.5.0(picomatch@4.0.4)
picomatch: 4.0.4
fdir: 6.5.0(picomatch@4.0.3)
picomatch: 4.0.3
trim-lines@3.0.1: {}
@ -1667,12 +1661,12 @@ snapshots:
'@types/unist': 3.0.3
vfile-message: 4.0.3
vite@7.3.2:
vite@7.3.1:
dependencies:
esbuild: 0.27.4
fdir: 6.5.0(picomatch@4.0.4)
picomatch: 4.0.4
postcss: 8.5.15
fdir: 6.5.0(picomatch@4.0.3)
picomatch: 4.0.3
postcss: 8.5.8
rollup: 4.59.0
tinyglobby: 0.2.15
optionalDependencies:
@ -1684,7 +1678,7 @@ snapshots:
gray-matter: 4.0.3
qsu: 1.10.4
vitepress@2.0.0-alpha.16(postcss@8.5.15):
vitepress@2.0.0-alpha.16(postcss@8.5.8):
dependencies:
'@docsearch/css': 4.6.0
'@docsearch/js': 4.6.0
@ -1694,7 +1688,7 @@ snapshots:
'@shikijs/transformers': 3.23.0
'@shikijs/types': 3.23.0
'@types/markdown-it': 14.1.2
'@vitejs/plugin-vue': 6.0.5(vite@7.3.2)(vue@3.5.30)
'@vitejs/plugin-vue': 6.0.5(vite@7.3.1)(vue@3.5.30)
'@vue/devtools-api': 8.1.0
'@vue/shared': 3.5.30
'@vueuse/core': 14.2.1(vue@3.5.30)
@ -1703,10 +1697,10 @@ snapshots:
mark.js: 8.11.1
minisearch: 7.2.0
shiki: 3.23.0
vite: 7.3.2
vite: 7.3.1
vue: 3.5.30
optionalDependencies:
postcss: 8.5.15
postcss: 8.5.8
transitivePeerDependencies:
- '@types/node'
- async-validator

View file

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

View file

@ -4,7 +4,6 @@ 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 \
@ -24,6 +23,8 @@ RUN apt-get update \
libharfbuzz0b \
libpangoft2-1.0-0 \
libpangocairo-1.0-0 \
#Chromium
chromium-headless-shell \
# to work inside the container
locales \
build-essential \
@ -74,11 +75,6 @@ RUN apt-get update \
file \
# For MIME type detection
media-types \
# Chromium
&& if [ "$INSTALL_CHROMIUM" != "false" ]; then \
DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y \
chromium-headless-shell; \
fi \
&& rm -rf /var/lib/apt/lists/*
RUN sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen \

View file

@ -4,12 +4,9 @@ FROM python:${PYTHON_VERSION}-slim-${DEBIAN_BASE} AS base
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 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}
@ -28,6 +25,8 @@ RUN useradd -ms /bin/bash frappe \
libharfbuzz0b \
libpangoft2-1.0-0 \
libpangocairo-1.0-0 \
#Chromium
chromium-headless-shell \
# For backups
restic \
gpg \
@ -49,7 +48,6 @@ 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 \
@ -62,11 +60,6 @@ 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 \
@ -85,6 +78,7 @@ RUN useradd -ms /bin/bash frappe \
&& chmod 755 /usr/local/bin/nginx-entrypoint.sh \
&& chmod 644 /templates/nginx/frappe.conf.template
COPY resources/core/nginx/security_headers.conf /etc/nginx/snippets/security_headers.conf
FROM base AS builder
@ -119,16 +113,18 @@ 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-16
ARG FRAPPE_PATH=https://github.com/frappe/frappe
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 \
RUN export APP_INSTALL_ARGS="" && \
if [ -n "${APPS_JSON_BASE64}" ]; then \
export APP_INSTALL_ARGS="--apps_path=/opt/frappe/apps.json"; \
fi && \
bench init ${APP_INSTALL_ARGS}\
@ -151,24 +147,21 @@ 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" \
]
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"]
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" \
]

View file

@ -1,18 +1,21 @@
ARG FRAPPE_BRANCH=version-16
ARG FRAPPE_IMAGE_PREFIX=frappe
FROM ${FRAPPE_IMAGE_PREFIX}/build:${FRAPPE_BRANCH} AS builder
FROM frappe/build:${FRAPPE_BRANCH} AS builder
ARG FRAPPE_BRANCH=version-16
ARG FRAPPE_PATH=https://github.com/frappe/frappe
ARG CACHE_BUST=""
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
USER frappe
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 \
RUN export APP_INSTALL_ARGS="" && \
if [ -n "${APPS_JSON_BASE64}" ]; then \
export APP_INSTALL_ARGS="--apps_path=/opt/frappe/apps.json"; \
fi && \
bench init ${APP_INSTALL_ARGS}\
@ -27,7 +30,7 @@ RUN --mount=type=secret,id=apps_json,target=/opt/frappe/apps.json,uid=1000,gid=1
echo "{}" > sites/common_site_config.json && \
find apps -mindepth 1 -path "*/.git" | xargs rm -fr
FROM ${FRAPPE_IMAGE_PREFIX}/base:${FRAPPE_BRANCH} AS backend
FROM frappe/base:${FRAPPE_BRANCH} AS backend
USER frappe
@ -35,24 +38,21 @@ 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" \
]
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"]
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" \
]

View file

@ -4,8 +4,6 @@ FROM python:${PYTHON_VERSION}-slim-${DEBIAN_BASE} AS base
ARG WKHTMLTOPDF_VERSION=0.12.6.1-3
ARG WKHTMLTOPDF_DISTRO=bookworm
ARG INSTALL_CHROMIUM=true
ARG NODE_VERSION=24.13.0
ENV NVM_DIR=/home/frappe/.nvm
ENV PATH=${NVM_DIR}/versions/node/v${NODE_VERSION}/bin/:${PATH}
@ -24,6 +22,8 @@ RUN useradd -ms /bin/bash frappe \
libharfbuzz0b \
libpangoft2-1.0-0 \
libpangocairo-1.0-0 \
#Chromium
chromium-headless-shell \
# For backups
restic \
gpg \
@ -45,7 +45,6 @@ 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,11 +57,6 @@ 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 \
@ -82,7 +76,6 @@ RUN useradd -ms /bin/bash frappe \
COPY resources/core/nginx/nginx-template.conf /templates/nginx/frappe.conf.template
COPY resources/core/nginx/nginx-entrypoint.sh /usr/local/bin/nginx-entrypoint.sh
COPY resources/core/nginx/security_headers.conf /etc/nginx/snippets/security_headers.conf
RUN chmod 755 /usr/local/bin/nginx-entrypoint.sh
FROM base AS build
@ -142,24 +135,21 @@ 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" \
]
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"]
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" \
]

View file

@ -13,6 +13,7 @@ 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,6 +20,7 @@ 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

@ -1,44 +0,0 @@
# 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

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

27
pwd.yml
View file

@ -1,6 +1,6 @@
services:
backend:
image: frappe/erpnext:v16.23.0
image: frappe/erpnext:v16.10.1
networks:
- frappe_network
deploy:
@ -16,7 +16,7 @@ services:
MARIADB_ROOT_PASSWORD: admin
configurator:
image: frappe/erpnext:v16.23.0
image: frappe/erpnext:v16.10.1
networks:
- frappe_network
deploy:
@ -45,7 +45,7 @@ services:
- logs:/home/frappe/frappe-bench/logs
create-site:
image: frappe/erpnext:v16.23.0
image: frappe/erpnext:v16.10.1
networks:
- frappe_network
deploy:
@ -78,15 +78,13 @@ 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:11.8
image: mariadb:10.6
networks:
- frappe_network
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
start_period: 5s
interval: 5s
timeout: 5s
retries: 5
test: mysqladmin ping -h localhost --password=admin
interval: 1s
retries: 20
deploy:
restart_policy:
condition: on-failure
@ -94,6 +92,7 @@ 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
@ -101,7 +100,7 @@ services:
- db-data:/var/lib/mysql
frontend:
image: frappe/erpnext:v16.23.0
image: frappe/erpnext:v16.10.1
networks:
- frappe_network
depends_on:
@ -127,7 +126,7 @@ services:
- "8080:8080"
queue-long:
image: frappe/erpnext:v16.23.0
image: frappe/erpnext:v16.10.1
networks:
- frappe_network
deploy:
@ -146,7 +145,7 @@ services:
FRAPPE_REDIS_QUEUE: redis://redis-queue:6379
queue-short:
image: frappe/erpnext:v16.23.0
image: frappe/erpnext:v16.10.1
networks:
- frappe_network
deploy:
@ -183,7 +182,7 @@ services:
condition: on-failure
scheduler:
image: frappe/erpnext:v16.23.0
image: frappe/erpnext:v16.10.1
networks:
- frappe_network
deploy:
@ -197,7 +196,7 @@ services:
- logs:/home/frappe/frappe-bench/logs
websocket:
image: frappe/erpnext:v16.23.0
image: frappe/erpnext:v16.10.1
networks:
- frappe_network
deploy:

View file

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

View file

@ -1,12 +0,0 @@
#!/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

@ -15,7 +15,6 @@ map $http_x_forwarded_proto $proxy_x_forwarded_proto {
server {
listen 8080;
server_name ${FRAPPE_SITE_NAME_HEADER};
absolute_redirect off;
root /home/frappe/frappe-bench/sites;
proxy_buffer_size 128k;

View file

@ -1,20 +0,0 @@
#!/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