mirror of
https://github.com/frappe/frappe_docker.git
synced 2026-06-17 13:55:08 +00:00
Merge branch 'main' into main
This commit is contained in:
commit
4dab3effc1
115 changed files with 6946 additions and 1044 deletions
14
.github/scripts/get_latest_tags.py
vendored
14
.github/scripts/get_latest_tags.py
vendored
|
|
@ -9,7 +9,7 @@ import sys
|
|||
from typing import Literal
|
||||
|
||||
Repo = Literal["frappe", "erpnext"]
|
||||
MajorVersion = Literal["12", "13", "14", "15", "develop"]
|
||||
MajorVersion = Literal["12", "13", "14", "15", "16", "develop"]
|
||||
|
||||
|
||||
def get_latest_tag(repo: Repo, version: MajorVersion) -> str:
|
||||
|
|
@ -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}))
|
||||
|
||||
|
|
@ -57,7 +64,7 @@ def main(_args: list[str]) -> int:
|
|||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--repo", choices=["frappe", "erpnext"], required=True)
|
||||
parser.add_argument(
|
||||
"--version", choices=["12", "13", "14", "15", "develop"], required=True
|
||||
"--version", choices=["12", "13", "14", "15", "16", "develop"], required=True
|
||||
)
|
||||
args = parser.parse_args(_args)
|
||||
|
||||
|
|
@ -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
189
.github/workflows/app-build-image.yml
vendored
Normal 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 }}
|
||||
35
.github/workflows/build_develop.yml
vendored
35
.github/workflows/build_develop.yml
vendored
|
|
@ -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.11.6
|
||||
node_version: 18.18.2
|
||||
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
|
||||
|
|
|
|||
118
.github/workflows/build_stable.yml
vendored
118
.github/workflows/build_stable.yml
vendored
|
|
@ -1,116 +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 }}
|
||||
|
||||
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@v4
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v5
|
||||
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 15 patch --remote origin --ci
|
||||
delegate:
|
||||
uses: ./.github/workflows/core-build-stable.yml
|
||||
permissions:
|
||||
contents: write
|
||||
packages: write
|
||||
secrets: inherit
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -20,16 +20,16 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
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.8.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.8.0
|
||||
uses: docker/bake-action@v7.2.0
|
||||
with:
|
||||
targets: bench
|
||||
push: true
|
||||
51
.github/workflows/core-build-develop.yml
vendored
Normal file
51
.github/workflows/core-build-develop.yml
vendored
Normal 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
148
.github/workflows/core-build-stable.yml
vendored
Normal 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
|
||||
|
|
@ -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
|
||||
|
|
@ -43,22 +60,26 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
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.8.0
|
||||
uses: docker/bake-action@v7.2.0
|
||||
with:
|
||||
source: .
|
||||
push: true
|
||||
|
|
@ -74,7 +95,7 @@ jobs:
|
|||
REGISTRY_USER: localhost:5000/frappe
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v5
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: "3.10"
|
||||
|
||||
|
|
@ -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.8.0
|
||||
with:
|
||||
push: true
|
||||
set: "*.platform=linux/amd64,linux/arm64"
|
||||
92
.github/workflows/core-publish-images.yml
vendored
Normal file
92
.github/workflows/core-publish-images.yml
vendored
Normal 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
66
.github/workflows/docs-publish-site.yml
vendored
Normal 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
|
||||
6
.github/workflows/lint.yml
vendored
6
.github/workflows/lint.yml
vendored
|
|
@ -13,16 +13,16 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v5
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: "3.10.6"
|
||||
|
||||
# For shfmt pre-commit hook
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: "^1.14"
|
||||
|
||||
|
|
|
|||
4
.github/workflows/pre-commit-autoupdate.yml
vendored
4
.github/workflows/pre-commit-autoupdate.yml
vendored
|
|
@ -10,13 +10,13 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Update pre-commit hooks
|
||||
uses: vrslev/pre-commit-autoupdate@v1.0.0
|
||||
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@v7
|
||||
uses: peter-evans/create-pull-request@v8
|
||||
with:
|
||||
branch: pre-commit-autoupdate
|
||||
title: "chore(deps): Update pre-commit hooks"
|
||||
|
|
|
|||
3
.github/workflows/stale.yml
vendored
3
.github/workflows/stale.yml
vendored
|
|
@ -9,10 +9,11 @@ jobs:
|
|||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v9
|
||||
- uses: actions/stale@v10
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
stale-issue-message: This issue has been automatically marked as stale. You have a week to explain why you believe this is an error.
|
||||
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
4
.gitignore
vendored
|
|
@ -28,3 +28,7 @@ venv
|
|||
|
||||
# NodeJS
|
||||
node_modules
|
||||
|
||||
# VitePress
|
||||
**/.vitepress/dist
|
||||
**/.vitepress/cache
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -6,6 +6,45 @@ On each PR that contains changes relevant to Docker builds, images are being bui
|
|||
|
||||
> :evergreen_tree: Please be considerate when pushing commits and opening PR for multiple branches, as the process of building images uses energy and contributes to global warming.
|
||||
|
||||
## Pull Request Process
|
||||
|
||||
1. Test builds locally before submitting
|
||||
2. Follow conventional commit format
|
||||
3. Update documentation if needed
|
||||
4. Ensure all pre-commit checks pass
|
||||
5. Reference related issues in PR description
|
||||
|
||||
## Commit Message Convention
|
||||
|
||||
We recommend [Conventional Commits](https://www.conventionalcommits.org/) for clear and semantic commit history.
|
||||
|
||||
Format: `<type>(<scope>): <description>`
|
||||
|
||||
**Types:**
|
||||
|
||||
- `feat`: New feature
|
||||
- `fix`: Bug fix
|
||||
- `docs`: Documentation changes
|
||||
- `style`: Code style changes (formatting, missing semicolons, etc.)
|
||||
- `refactor`: Code refactoring
|
||||
- `test`: Adding or updating tests
|
||||
- `chore`: Maintenance tasks (dependencies, build config, etc.)
|
||||
- `ci`: CI/CD changes
|
||||
|
||||
**Examples:**
|
||||
|
||||
```
|
||||
chore(deps): bump wkhtmltopdf version
|
||||
fix(ci): correct buildx cache configuration
|
||||
docs(contributing): add conventional commits guidelines
|
||||
```
|
||||
|
||||
## Branch Naming
|
||||
|
||||
- `feature/<description>` - New features
|
||||
- `fix/<description>` - Bug fixes
|
||||
- `docs/<description>` - Documentation updates
|
||||
|
||||
## Lint
|
||||
|
||||
We use `pre-commit` framework to lint the codebase before committing.
|
||||
|
|
@ -61,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,
|
||||
|
||||
```
|
||||

|
||||
```
|
||||
|
||||
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
|
||||
|
||||
|
|
@ -78,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
25
MAINTAINERS.md
Normal 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.
|
||||
159
README.md
159
README.md
|
|
@ -1,23 +1,89 @@
|
|||
[](https://github.com/frappe/frappe_docker/actions/workflows/build_stable.yml)
|
||||
[](https://github.com/frappe/frappe_docker/actions/workflows/build_develop.yml)
|
||||
<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>
|
||||
|
||||
Everything about [Frappe](https://github.com/frappe/frappe) and [ERPNext](https://github.com/frappe/erpnext) in containers.
|
||||
## What is this?
|
||||
|
||||
# Getting Started
|
||||
This repository is the official container setup for Frappe applications.
|
||||
|
||||
To get started you need [Docker](https://docs.docker.com/get-docker/), [docker-compose](https://docs.docker.com/compose/), and [git](https://docs.github.com/en/get-started/getting-started-with-git/set-up-git) setup on your machine. For Docker basics and best practices refer to Docker's [documentation](http://docs.docker.com).
|
||||
It provides Docker images, Compose configurations, and documentation for running Frappe applications, including ERPNext, CRM, Helpdesk, and other Frappe apps, in containers.
|
||||
|
||||
Once completed, chose one of the following two sections for next steps.
|
||||
Use it if you want to:
|
||||
|
||||
### Try in Play With Docker
|
||||
- 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
|
||||
|
||||
To play in an already set up sandbox, in your browser, click the button below:
|
||||
## Repository Structure
|
||||
|
||||
<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>
|
||||
```bash
|
||||
frappe_docker/
|
||||
├── docs/ # Complete documentation
|
||||
├── overrides/ # Docker Compose configurations for different scenarios
|
||||
├── compose.yaml # Base Compose File for production setups
|
||||
├── pwd.yml # Single Compose File for quick disposable demo
|
||||
├── images/ # Dockerfiles for building Frappe images
|
||||
├── development/ # Development environment configurations
|
||||
├── devcontainer-example/ # VS Code devcontainer setup
|
||||
└── resources/ # Helper scripts and configuration templates
|
||||
```
|
||||
|
||||
### Try on your Dev environment
|
||||
> This section describes the structure of **this repository**, not the Frappe framework itself.
|
||||
|
||||
### Key Components
|
||||
|
||||
- `docs/` - Canonical documentation for all deployment and operational workflows
|
||||
- `overrides/` - Opinionated Compose overrides for common deployment patterns
|
||||
- `compose.yaml` - Base compose file for production setups (production)
|
||||
- `pwd.yml` - Disposable demo environment (non-production)
|
||||
|
||||
## 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/).
|
||||
|
||||
### Recommended entry points:
|
||||
|
||||
- **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
|
||||
|
||||
- [Docker](https://docs.docker.com/get-docker/)
|
||||
- [Docker Compose v2](https://docs.docker.com/compose/)
|
||||
- [git](https://docs.github.com/en/get-started/getting-started-with-git/set-up-git)
|
||||
|
||||
> For Docker basics and best practices refer to Docker's [documentation](http://docs.docker.com)
|
||||
|
||||
## Demo setup
|
||||
|
||||
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 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:
|
||||
|
||||
|
|
@ -26,65 +92,26 @@ git clone https://github.com/frappe/frappe_docker
|
|||
cd frappe_docker
|
||||
```
|
||||
|
||||
Then run: `docker compose -f pwd.yml up -d`
|
||||
Then run:
|
||||
|
||||
### To run on ARM64 architecture follow this instructions
|
||||
```sh
|
||||
docker compose -f pwd.yml up -d
|
||||
```
|
||||
|
||||
After cloning the repo run this command to build multi-architecture images specifically for ARM64.
|
||||
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`)
|
||||
|
||||
`docker buildx bake --no-cache --set "*.platform=linux/arm64"`
|
||||
## Contributing
|
||||
|
||||
and then
|
||||
|
||||
- add `platform: linux/arm64` to all services in the `pwd.yml`
|
||||
- replace the current specified versions of erpnext image on `pwd.yml` with `:latest`
|
||||
|
||||
Then run: `docker compose -f pwd.yml up -d`
|
||||
|
||||
## Final steps
|
||||
|
||||
Wait for 5 minutes for ERPNext site to be created or check `create-site` container logs before opening browser on port 8080. (username: `Administrator`, password: `admin`)
|
||||
|
||||
If you ran in a Dev Docker environment, to view container logs: `docker compose -f pwd.yml logs -f create-site`. Don't worry about some of the initial error messages, some services take a while to become ready, and then they go away.
|
||||
|
||||
# Documentation
|
||||
|
||||
### [Frequently Asked Questions](https://github.com/frappe/frappe_docker/wiki/Frequently-Asked-Questions)
|
||||
|
||||
### [Production](#production)
|
||||
|
||||
- [List of containers](docs/list-of-containers.md)
|
||||
- [Single Compose Setup](docs/single-compose-setup.md)
|
||||
- [Environment Variables](docs/environment-variables.md)
|
||||
- [Single Server Example](docs/single-server-example.md)
|
||||
- [Setup Options](docs/setup-options.md)
|
||||
- [Site Operations](docs/site-operations.md)
|
||||
- [Backup and Push Cron Job](docs/backup-and-push-cronjob.md)
|
||||
- [Port Based Multi Tenancy](docs/port-based-multi-tenancy.md)
|
||||
- [Migrate from multi-image setup](docs/migrate-from-multi-image-setup.md)
|
||||
- [running on linux/mac](docs/setup_for_linux_mac.md)
|
||||
- [TLS for local deployment](docs/tls-for-local-deployment.md)
|
||||
|
||||
### [Custom Images](#custom-images)
|
||||
|
||||
- [Custom Apps](docs/custom-apps.md)
|
||||
- [Custom Apps with podman](docs/custom-apps-podman.md)
|
||||
- [Build Version 10 Images](docs/build-version-10-images.md)
|
||||
|
||||
### [Development](#development)
|
||||
|
||||
- [Development using containers](docs/development.md)
|
||||
- [Bench Console and VSCode Debugger](docs/bench-console-and-vscode-debugger.md)
|
||||
- [Connect to localhost services](docs/connect-to-localhost-services-from-containers-for-local-app-development.md)
|
||||
|
||||
### [Troubleshoot](docs/troubleshoot.md)
|
||||
|
||||
# Contributing
|
||||
|
||||
If you want to contribute to this repo refer to [CONTRIBUTING.md](CONTRIBUTING.md)
|
||||
Contributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
|
||||
|
||||
This repository is only for container related stuff. You also might want to contribute to:
|
||||
|
||||
- [Frappe framework](https://github.com/frappe/frappe#contributing),
|
||||
- [ERPNext](https://github.com/frappe/erpnext#contributing),
|
||||
## Resources
|
||||
|
||||
- [Frappe framework](https://github.com/frappe/frappe),
|
||||
- [ERPNext](https://github.com/frappe/erpnext),
|
||||
- [Frappe Bench](https://github.com/frappe/bench).
|
||||
|
||||
## License
|
||||
|
||||
This repository is licensed under the MIT License. See [LICENSE](LICENSE) for details.
|
||||
|
|
|
|||
|
|
@ -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/custom-apps.md
|
||||
# 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
|
||||
|
|
|
|||
|
|
@ -26,11 +26,42 @@
|
|||
"ms-vscode.live-server",
|
||||
"grapecity.gc-excelviewer",
|
||||
"mtxr.sqltools",
|
||||
"mtxr.sqltools-driver-mysql",
|
||||
"visualstudioexptteam.vscodeintellicode"
|
||||
]
|
||||
],
|
||||
"settings": {
|
||||
"terminal.integrated.profiles.linux": {
|
||||
"frappe bash": {
|
||||
"path": "/bin/bash"
|
||||
}
|
||||
},
|
||||
"terminal.integrated.defaultProfile.linux": "frappe bash",
|
||||
"debug.node.autoAttach": "disabled",
|
||||
"sqltools.connections": [
|
||||
{
|
||||
"mysqlOptions": {
|
||||
"authProtocol": "default",
|
||||
"enableSsl": "Disabled"
|
||||
},
|
||||
"ssh": "Disabled",
|
||||
"previewLimit": 50,
|
||||
"server": "mariadb",
|
||||
"port": 3306,
|
||||
"driver": "MariaDB",
|
||||
"name": "MariaDB",
|
||||
"username": "root",
|
||||
"password": "123",
|
||||
"database": "mysql"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"dockerComposeFile": "./docker-compose.yml",
|
||||
"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"
|
||||
]
|
||||
|
|
|
|||
|
|
@ -1,21 +1,19 @@
|
|||
version: "3.7"
|
||||
services:
|
||||
mariadb:
|
||||
image: docker.io/mariadb:10.6
|
||||
platform: linux/amd64
|
||||
image: docker.io/mariadb:11.8
|
||||
command:
|
||||
- --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
|
||||
volumes:
|
||||
- mariadb-data:/var/lib/mysql
|
||||
|
||||
# Enable PostgreSQL only if you use it, see development/README.md for more information.
|
||||
# postgresql:
|
||||
# image: postgres:11.8
|
||||
# image: postgres:14
|
||||
# environment:
|
||||
# POSTGRES_PASSWORD: 123
|
||||
# volumes:
|
||||
|
|
@ -38,15 +36,14 @@ services:
|
|||
|
||||
redis-cache:
|
||||
image: docker.io/redis:alpine
|
||||
platform: linux/amd64
|
||||
|
||||
redis-queue:
|
||||
image: docker.io/redis:alpine
|
||||
platform: linux/amd64
|
||||
|
||||
frappe:
|
||||
image: docker.io/frappe/bench:latest
|
||||
platform: linux/amd64
|
||||
# If you want to build the current bench image the Containerfile is in this Repo.
|
||||
# build: ../images/bench
|
||||
command: sleep infinity
|
||||
environment:
|
||||
- SHELL=/bin/bash
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[
|
||||
{
|
||||
"url": "https://github.com/frappe/erpnext.git",
|
||||
"branch": "version-15"
|
||||
"branch": "version-16"
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,10 +6,10 @@ variable "REGISTRY_USER" {
|
|||
}
|
||||
|
||||
variable PYTHON_VERSION {
|
||||
default = "3.11.6"
|
||||
default = "3.14.2"
|
||||
}
|
||||
variable NODE_VERSION {
|
||||
default = "18.18.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 = [
|
||||
|
|
|
|||
29
docs/.vitepress/config.mts
Normal file
29
docs/.vitepress/config.mts
Normal 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),
|
||||
);
|
||||
92
docs/01-getting-started/00-introduction.md
Normal file
92
docs/01-getting-started/00-introduction.md
Normal 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
|
||||
126
docs/01-getting-started/01-choosing-a-deployment-method.md
Normal file
126
docs/01-getting-started/01-choosing-a-deployment-method.md
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
---
|
||||
title: Choosing a Method
|
||||
---
|
||||
|
||||
# Choosing a Deployment or Development Method
|
||||
|
||||
This repository (`frappe_docker`) supports **multiple ways to run Frappe using Docker**.
|
||||
Each method is designed for a **specific purpose**, and they are **not interchangeable**.
|
||||
|
||||
This document explains:
|
||||
|
||||
- All supported ways to use this repository
|
||||
- Which method to choose depending on your goal
|
||||
- Common misconceptions, especially around persistence and app installation
|
||||
|
||||
Reading this document **before following any setup guide** is strongly recommended.
|
||||
|
||||
## Overview
|
||||
|
||||
| Goal | Recommended Method | Production Ready |
|
||||
| ---------------------------- | ------------------------- | ---------------- |
|
||||
| Quick exploration | `pwd.yml` | ❌ |
|
||||
| Local development | VS Code Devcontainers | ❌ |
|
||||
| Automated production install | Easy Install Script | ✅ |
|
||||
| Manual production deployment | `compose.yml` + overrides | ✅ |
|
||||
|
||||
## 1. `pwd.yml` – Quick Test / Exploration Setup
|
||||
|
||||
The `pwd.yml` file is a **single, self-contained Docker Compose file** intended for:
|
||||
|
||||
- Trying out Frappe and ERPNext
|
||||
- Demos and short-lived test environments
|
||||
- Learning the basics without setup overhead
|
||||
|
||||
### Characteristics
|
||||
|
||||
- One Compose file
|
||||
- Minimal configuration
|
||||
- Fast startup
|
||||
- Disposable by design
|
||||
|
||||
### Limitations
|
||||
|
||||
- ❌ **Not intended for production**
|
||||
- ❌ **Not intended for development**
|
||||
- ❌ **Not suitable as a migration starting point**
|
||||
|
||||
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/development.md`](../05-development/01-development.md)
|
||||
|
||||
uses **VS Code Devcontainers** to provide a **local Frappe development environment**.
|
||||
|
||||
### Intended Use
|
||||
|
||||
- Developing Frappe or custom apps
|
||||
- Working with source code
|
||||
- Debugging and testing changes locally
|
||||
|
||||
### Key Differences from Other Setups
|
||||
|
||||
- Optimized for **interactive development**
|
||||
- Code is editable live
|
||||
- Containers are tailored for developer workflows
|
||||
- Not designed to represent a production environment
|
||||
|
||||
### Important Notes
|
||||
|
||||
- ❌ **Not a deployment method**
|
||||
- ❌ **Not intended for production**
|
||||
- ✔ The **correct way** to do local development with this repository
|
||||
|
||||
Using production-oriented setups (`pwd.yml` or `compose.yml`) for development is strongly discouraged.
|
||||
|
||||
## 3. Easy Install Script (from `frappe/bench`)
|
||||
|
||||
The Easy Install script provided in the [`frappe/bench`](https://github.com/frappe/bench) repository uses `frappe_docker` internally and automates a full deployment process.
|
||||
|
||||
It is comparable to what a **deployment pipeline** would perform.
|
||||
|
||||
### What It Does
|
||||
|
||||
- Installs Docker and prerequisites
|
||||
- Pulls and configures `frappe_docker`
|
||||
- Uses production-grade images and services
|
||||
- Reduces manual configuration
|
||||
|
||||
### Intended Use
|
||||
|
||||
- Production environments
|
||||
- Users who want a guided, automated installation
|
||||
- Server deployments with minimal manual steps
|
||||
|
||||
### Production Readiness
|
||||
|
||||
✔ **Yes** — suitable for real production systems
|
||||
✔ Uses the same components as the manual production setup
|
||||
|
||||
## 4. `compose.yml` + Overrides – Intended Production Setup
|
||||
|
||||
This is the **canonical production deployment method** for `frappe_docker`.
|
||||
|
||||
It uses:
|
||||
|
||||
- The main `compose.yml`
|
||||
- Override files from the `overrides/` directory
|
||||
|
||||
Detailed instructions are available in [`/docs/02-setup`](../02-setup/01-overview.md)
|
||||
|
||||
### Characteristics
|
||||
|
||||
- Explicit service definitions
|
||||
- Flexible and configurable
|
||||
- Designed for long-running production environments
|
||||
- Suitable for advanced and customized deployments
|
||||
|
||||
**This is the preferred approach for teams managing their own infrastructure.**
|
||||
|
||||
## Summary
|
||||
|
||||
- Each setup serves a **distinct purpose**
|
||||
- Development, testing, and production are **separate workflows**
|
||||
- Do not expect to evolve a disposable setup into production
|
||||
- Apps must be included **at build time**, not installed later ([Docker immutability](02-docker-immutability.md))
|
||||
52
docs/01-getting-started/02-docker-immutability.md
Normal file
52
docs/01-getting-started/02-docker-immutability.md
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
---
|
||||
title: Docker Immutability
|
||||
---
|
||||
|
||||
# Important Concept: Immutability and Persistence
|
||||
|
||||
A frequent source of confusion is how **Docker-based Frappe deployments handle persistence**.
|
||||
|
||||
### Containers Are Immutable
|
||||
|
||||
Docker containers are **not meant to be modified after they are built**.
|
||||
You should only change:
|
||||
|
||||
- Environment variables
|
||||
- Mounted volumes
|
||||
- The Docker image itself (via rebuild)
|
||||
|
||||
### What Is Persistent
|
||||
|
||||
Typically, only these paths are persisted:
|
||||
|
||||
- Site data (`/sites`)
|
||||
- Database storage
|
||||
|
||||
This allows you to:
|
||||
|
||||
- Create new sites
|
||||
- Run migrations
|
||||
- Perform backups and restores
|
||||
- Recreate containers safely
|
||||
|
||||
## Installing Apps After Deployment
|
||||
|
||||
### ❌ Not Supported
|
||||
|
||||
Installing apps into a running container is **not supported**.
|
||||
|
||||
`bench get-app` and `bench build` are examples of an common but unsupported actions.
|
||||
|
||||
### Why?
|
||||
|
||||
- Apps and assets are part of the **Docker image**
|
||||
- Runtime changes are lost on container recreation
|
||||
- This ensures reproducibility and stability
|
||||
|
||||
### Correct Workflow
|
||||
|
||||
1. Add the app to the image build configuration
|
||||
2. Rebuild the Docker image
|
||||
3. Redeploy the stack
|
||||
|
||||
This applies to **all production-oriented setups**.
|
||||
|
|
@ -1,22 +1,36 @@
|
|||
---
|
||||
title: Quick Start with Linux and Mac
|
||||
---
|
||||
|
||||
# How to install ERPNext on linux/mac using Frappe_docker ?
|
||||
|
||||
step1: clone the repo
|
||||
## Clone the repo
|
||||
|
||||
```
|
||||
```sh
|
||||
git clone https://github.com/frappe/frappe_docker
|
||||
cd frappe_docker
|
||||
```
|
||||
|
||||
step2: add platform: linux/amd64 to all services in the /pwd.yaml
|
||||
## buildx platform
|
||||
|
||||
here is the update pwd.yml file
|
||||
Run this command to build multi-architecture images specifically for ARM64:
|
||||
|
||||
```sh
|
||||
docker buildx bake --no-cache --set "*.platform=linux/arm64"
|
||||
```
|
||||
|
||||
## add platform to all services
|
||||
|
||||
- add platform: `linux/arm64` to all services in the `pwd.yml`
|
||||
- (replace the current specified versions of erpnext image on `pwd.yml` with `:latest`)
|
||||
|
||||
here is the example pwd.yml file:
|
||||
|
||||
```yml
|
||||
version: "3"
|
||||
|
||||
services:
|
||||
backend:
|
||||
image: frappe/erpnext:v15
|
||||
platform: linux/amd64
|
||||
image: frappe/erpnext:v16.19.1
|
||||
platform: linux/arm64
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
|
|
@ -25,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
|
||||
|
|
@ -54,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
|
||||
|
|
@ -86,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
|
||||
|
|
@ -99,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:
|
||||
|
|
@ -131,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
|
||||
|
|
@ -146,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
|
||||
|
|
@ -162,7 +175,7 @@ services:
|
|||
|
||||
redis-queue:
|
||||
image: redis:6.2-alpine
|
||||
platform: linux/amd64
|
||||
platform: linux/arm64
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
|
|
@ -171,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
|
||||
|
|
@ -190,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
|
||||
|
|
@ -209,14 +222,10 @@ volumes:
|
|||
logs:
|
||||
```
|
||||
|
||||
step3: run the docker
|
||||
## Run the compose file
|
||||
|
||||
```
|
||||
cd frappe_docker
|
||||
```
|
||||
|
||||
```
|
||||
docker-compose -f ./pwd.yml up
|
||||
```sh
|
||||
docker compose -f pwd.yml up
|
||||
```
|
||||
|
||||
---
|
||||
|
|
@ -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`.
|
||||
3
docs/01-getting-started/index.md
Normal file
3
docs/01-getting-started/index.md
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
---
|
||||
title: Getting Started
|
||||
---
|
||||
53
docs/02-setup/01-overview.md
Normal file
53
docs/02-setup/01-overview.md
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
---
|
||||
title: Setup Overview
|
||||
---
|
||||
|
||||
The purpose of this document is to give you an overview of how the Frappe Docker containers are structured.
|
||||
|
||||
# 🐳 Images
|
||||
|
||||
There are **four predefined Dockerfiles** available in the `/images` directory.
|
||||
|
||||
| Dockerfile | Ingredients | Purpose & Use Case |
|
||||
| -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **bench** | Sets up only the Bench CLI. | Used for **development** or debugging. Provides the command-line tooling but does not include runtime services. |
|
||||
| **custom** | Multi-purpose Python backend built from a plain Python image. Includes everything needed to run a Frappe instance via a Compose setup. Installs apps defined in `apps.json`. | Suitable for **production** and **testing**. Ideal when you need control over dependencies (e.g. trying new Python or Node versions). |
|
||||
| **layered** | Final contents are the same as `custom`, but it is based on **prebuilt images from [Docker Hub](https://hub.docker.com/u/frappe)**. | Great for **production builds** when you’re fine with the dependency versions managed by Frappe. Builds much faster since the base layers are already prepared. |
|
||||
| **production** | Similar to `custom` (built from a Python base image), but installs **only Frappe and ERPNext**. Not customizable with `apps.json`. | Best for **quick starts** or exploration. For real deployments or CI/CD pipelines, `custom` or `layered` are preferred because they offer more flexibility. |
|
||||
|
||||
---
|
||||
|
||||
These images include everything needed to run all processes required by the Frappe framework
|
||||
(see [Bench Procfile reference](https://frappeframework.com/docs/v14/user/en/bench/resources/bench-procfile)).
|
||||
|
||||
- The `bench` image only sets up the CLI tool.
|
||||
- The other images (`custom`, `layered`, and `production`) go further — enabling a nearly **plug-and-play** setup for ERPNext and custom apps.
|
||||
|
||||
> We use [multi-stage builds](https://docs.docker.com/develop/develop-images/multistage-build/) and [Docker Buildx](https://docs.docker.com/engine/reference/commandline/buildx/) to maximize layer reuse and make our builds more efficient.
|
||||
|
||||
# 🏗️ Compose
|
||||
|
||||
Once images are built, containers are orchestrated using a [compose file](https://docs.docker.com/compose/compose-file/). The main compose.yaml provides core services, networking, and volumes for any Frappe setup.
|
||||
|
||||
## 🛠️ Services
|
||||
|
||||
| Service | Role | Purpose |
|
||||
| ---------------- | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **configurator** | Setup | Updates `common_site_config.json` so Frappe knows how to access db and redis. It is executed on every `docker-compose up` (and exited immediately). Other services start after this container exits successfully |
|
||||
| **backend** | Runtime | [Werkzeug server](https://werkzeug.palletsprojects.com/en/2.0.x/) |
|
||||
| **frontend** | Proxy | [nginx](https://www.nginx.com) server that serves JS/CSS assets and routes incoming requests |
|
||||
| **websocket** | Real-time | Node server that runs [Socket.IO](https://socket.io) |
|
||||
| **queue-\_** | Background Jobs | Python servers that run job queues using [rq](https://python-rq.org) |
|
||||
| **scheduler** | Task Automation | Python server that runs tasks on schedule using [schedule](https://schedule.readthedocs.io/en/stable/) |
|
||||
|
||||
## 🧩 Overrides
|
||||
|
||||
Additional functionality can be added using [overrides](https://docs.docker.com/compose/extends/). These files modify existing services or add new ones without changing the main `compose.yaml`.
|
||||
|
||||
Example: The main compose file has no database service, but `compose.mariadb.yaml` adds MariaDB. See [overrides.md](05-overrides.md) for the complete list of available overrides and how to use them.
|
||||
|
||||
---
|
||||
|
||||
**Next:** [Build Setup →](02-build-setup.md)
|
||||
|
||||
**See also:** [Setup Examples](06-setup-examples.md) for practical deployment scenarios.
|
||||
153
docs/02-setup/02-build-setup.md
Normal file
153
docs/02-setup/02-build-setup.md
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
---
|
||||
title: Build Setup
|
||||
---
|
||||
|
||||
This guide walks you through building Frappe images from the repository resources.
|
||||
|
||||
# Prerequisites
|
||||
|
||||
- git
|
||||
- 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
|
||||
git clone https://github.com/frappe/frappe_docker
|
||||
cd frappe_docker
|
||||
```
|
||||
|
||||
# Define custom apps
|
||||
|
||||
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:
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"url": "https://github.com/frappe/erpnext",
|
||||
"branch": "version-16"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/frappe/hrms",
|
||||
"branch": "version-16"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/frappe/helpdesk",
|
||||
"branch": "main"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
# Build custom images
|
||||
|
||||
## 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-16 \
|
||||
--secret=id=apps_json,src=apps.json \
|
||||
--tag=custom:16 \
|
||||
--file=images/layered/Containerfile .
|
||||
```
|
||||
|
||||
`Podman`:
|
||||
|
||||
```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 \
|
||||
--file=images/layered/Containerfile .
|
||||
```
|
||||
|
||||
## Automated
|
||||
|
||||
This repository is fully suited for automated builds, i.e. using CI/CD pipelines.
|
||||
|
||||
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.
|
||||
|
||||
```bash
|
||||
cp example.env custom.env
|
||||
```
|
||||
|
||||
Edit `custom.env` to customize variables for your setup. The template includes common variables, but you can add, modify, or remove any as needed. See [env-variables.md](04-env-variables.md) for detailed descriptions of all available variables.
|
||||
|
||||
For this setup, make sure **at least** the following values are added to `custom.env`:
|
||||
|
||||
```txt
|
||||
CUSTOM_IMAGE=custom
|
||||
CUSTOM_TAG=16
|
||||
PULL_POLICY=missing
|
||||
```
|
||||
|
||||
> The `CUSTOM_*` variables ensure the image reference points to the recently built image.
|
||||
> `PULL_POLICY` ensures Docker does not attempt to pull the image, but instead uses the locally built one (the default pull policy is `always`).
|
||||
|
||||
**⚠️ 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
|
||||
|
||||
Combine the base compose file with appropriate overrides for your use case. This example adds MariaDB, Redis, and exposes ports on `:8080`:
|
||||
|
||||
```bash
|
||||
docker compose --env-file custom.env \
|
||||
-f compose.yaml \
|
||||
-f overrides/compose.mariadb.yaml \
|
||||
-f overrides/compose.redis.yaml \
|
||||
-f overrides/compose.noproxy.yaml \
|
||||
config > compose.custom.yaml
|
||||
```
|
||||
|
||||
This generates `compose.custom.yaml`, which you'll use to start all containers. Customize the overrides and environment variables according to your requirements.
|
||||
|
||||
> **NOTE**: podman compose is just a wrapper, it uses docker-compose if it is available or podman-compose if not. podman-compose have an issue reading .env files ([Issue](https://github.com/containers/podman-compose/issues/475)) and might create an issue when running the containers.
|
||||
|
||||
---
|
||||
|
||||
**Next:** [Start Setup →](03-start-setup.md)
|
||||
|
||||
**Back:** [Container Overview ←](01-overview.md)
|
||||
|
||||
**See also:** [Setup Examples](06-setup-examples.md) for practical deployment scenarios.
|
||||
66
docs/02-setup/03-start-setup.md
Normal file
66
docs/02-setup/03-start-setup.md
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
---
|
||||
title: Start Container
|
||||
---
|
||||
|
||||
# start Container
|
||||
|
||||
Once your compose file is ready, start all containers with a single command:
|
||||
|
||||
```bash
|
||||
docker compose -p frappe -f compose.custom.yaml up -d
|
||||
```
|
||||
|
||||
```bash
|
||||
podman-compose --in-pod=1 --project-name frappe -f compose.custom.yaml up -d
|
||||
```
|
||||
|
||||
The `-p` (or `--project-name`) flag names the project `frappe`, allowing you to easily reference and manage all containers together.
|
||||
|
||||
# Create a site and install apps
|
||||
|
||||
Frappe is now running, but it's not yet configured. You need to create a site and install your apps.
|
||||
|
||||
## Basic site creation
|
||||
|
||||
```bash
|
||||
docker compose -p frappe exec backend bench new-site <sitename> --mariadb-user-host-login-scope='172.%.%.%'
|
||||
docker compose -p frappe exec backend bench --site <sitename> install-app erpnext
|
||||
```
|
||||
|
||||
```bash
|
||||
podman exec -ti erpnext_backend_1 /bin/bash
|
||||
bench new-site <sitename> --mariadb-user-host-login-scope='172.%.%.%'
|
||||
bench --site <sitename> install-app erpnext
|
||||
```
|
||||
|
||||
Replace `<sitename>` with your desired site name.
|
||||
|
||||
## Create site with app installation
|
||||
|
||||
You can install apps during site creation:
|
||||
|
||||
```bash
|
||||
docker compose -p frappe exec backend bench new-site <sitename> \
|
||||
--mariadb-user-host-login-scope='%' \
|
||||
--db-root-password <db-password> \
|
||||
--admin-password <admin-password> \
|
||||
--install-app erpnext
|
||||
```
|
||||
|
||||
> **Note:** Wait for the `db` service to start and `configurator` to exit before trying to create a new site. Usually this takes up to 10 seconds.
|
||||
|
||||
For more site operations, refer to [site operations](../04-operations/01-site-operations.md).
|
||||
|
||||
> ## Understanding the MariaDB User Scope
|
||||
>
|
||||
> The flag --mariadb-user-host-login-scope='172.%.%.%' allows database connections from any IP address within the 172.0.0.0/8 range. This includes all containers and virtual machines running on your machine.
|
||||
>
|
||||
> **Why is this necessary?** Docker and Podman assign dynamic IP addresses to containers. If you set a fixed IP address instead, database connections will fail when the container restarts and receives a new IP. The wildcard pattern ensures connections always work, regardless of IP changes.
|
||||
>
|
||||
> **Security note:** This scope is sufficient because only the backend container accesses the database. If you need external database access, adjust the scope accordingly, but be cautious with overly permissive settings.
|
||||
|
||||
---
|
||||
|
||||
**Back:** [Build Setup →](02-build-setup.md)
|
||||
|
||||
**Next:** [Setup Examples →](06-setup-examples.md)
|
||||
160
docs/02-setup/04-env-variables.md
Normal file
160
docs/02-setup/04-env-variables.md
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
---
|
||||
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.
|
||||
|
||||
**Getting Started:**
|
||||
|
||||
```bash
|
||||
cp example.env .env
|
||||
```
|
||||
|
||||
Then edit `.env` and set variables according to your needs.
|
||||
|
||||
---
|
||||
|
||||
## Required Variables
|
||||
|
||||
| Variable | Purpose | Example | Notes |
|
||||
| ----------------- | ------------------------------------------------ | -------------------------------- | ---------------------------------------------------------------- |
|
||||
| `FRAPPE_PATH` | Frappe framework path | https://github.com/frappe/frappe | |
|
||||
| `FRAPPE_BRANCH` | Frappe Branch | `version-15` | See [Frappe releases](https://github.com/frappe/frappe/releases) |
|
||||
| `ERPNEXT_VERSION` | ERPNext release version | `v15.67.0` | Required although its never used |
|
||||
| `DB_PASSWORD` | Password for database root (MariaDB or Postgres) | `secure_password_123` | Not needed if using `DB_PASSWORD_SECRETS_FILE` |
|
||||
|
||||
---
|
||||
|
||||
## Database Configuration
|
||||
|
||||
| Variable | Purpose | Default | When to Set |
|
||||
| -------------------------- | ----------------------------------------- | ------------------------------------ | ---------------------------------- |
|
||||
| `DB_PASSWORD` | Database root user password | 123 | Always (unless using secrets file) |
|
||||
| `DB_PASSWORD_SECRETS_FILE` | Path to file containing database password | — | Setup mariadb-secrets overrider |
|
||||
| `DB_HOST` | Database hostname or IP | `db` (service name) | Only if using external database |
|
||||
| `DB_PORT` | Database port | `3306` (MariaDB) / `5432` (Postgres) | Only if using external database |
|
||||
|
||||
---
|
||||
|
||||
## Redis Configuration
|
||||
|
||||
| Variable | Purpose | Default | When to Set |
|
||||
| ------------- | --------------------------------------------------- | ---------------------------- | ------------------------------------- |
|
||||
| `REDIS_CACHE` | Redis hostname for caching | `redis-cache` (service name) | Only if using external Redis instance |
|
||||
| `REDIS_QUEUE` | Redis hostname for job queues and real-time updates | `redis-queue` (service name) | Only if using external Redis instance |
|
||||
|
||||
---
|
||||
|
||||
## Reverse Proxy and SSL (HTTPS) Configuration
|
||||
|
||||
### 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`:**
|
||||
|
||||
```bash
|
||||
# Single site
|
||||
SITES_RULE=Host(`mysite.example.com`)
|
||||
|
||||
# Multiple sites
|
||||
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
|
||||
|
||||
| Variable | Purpose | Default | When to Set |
|
||||
| ------------------------- | -------------------------------- | ---------------------------------------- | ----------------------------------------------- |
|
||||
| `FRAPPE_SITE_NAME_HEADER` | Site name for multi-tenant setup | `$host` (resolved from request hostname) | When accessing by IP or need explicit site name |
|
||||
|
||||
**Examples:**
|
||||
|
||||
If your site is named `mysite` but you want to access it via `127.0.0.1`:
|
||||
|
||||
```bash
|
||||
FRAPPE_SITE_NAME_HEADER=mysite
|
||||
```
|
||||
|
||||
If your site is named `example.com` and you access it via that domain, no need to set this (defaults to hostname).
|
||||
|
||||
---
|
||||
|
||||
## Image Configuration
|
||||
|
||||
| Variable | Purpose | Default | Notes |
|
||||
| ---------------- | ------------------------------ | --------------------- | ------------------------------------------------------- |
|
||||
| `CUSTOM_IMAGE` | Custom Docker image repository | Frappe official image | Leave empty to use default |
|
||||
| `CUSTOM_TAG` | Custom Docker image tag | Latest stable | Corresponds to `FRAPPE_VERSION` |
|
||||
| `PULL_POLICY` | Image pull behavior | `always` | Options: `always`, `never`, `if-not-present` |
|
||||
| `RESTART_POLICY` | Container restart behavior | `unless-stopped` | Options: `no`, `always`, `unless-stopped`, `on-failure` |
|
||||
|
||||
---
|
||||
|
||||
## 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}` |
|
||||
| `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`) |
|
||||
|
||||
### Real IP Configuration (Behind Proxy)
|
||||
|
||||
Use these variables when running behind a reverse proxy or load balancer:
|
||||
|
||||
| Variable | Purpose | Default |
|
||||
| ---------------------------- | ------------------------------------------------- | ----------------- |
|
||||
| `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` |
|
||||
35
docs/02-setup/05-overrides.md
Normal file
35
docs/02-setup/05-overrides.md
Normal file
|
|
@ -0,0 +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. |
|
||||
| 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 | | |
|
||||
142
docs/02-setup/06-setup-examples.md
Normal file
142
docs/02-setup/06-setup-examples.md
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
---
|
||||
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.
|
||||
|
||||
> **Note:** This setup is not for development. A complete development environment is available [here](../05-development/01-development.md).
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- [docker](https://docker.com/get-started)
|
||||
- [docker compose v2](https://docs.docker.com/compose/cli-command)
|
||||
- Cloned `frappe_docker` repository
|
||||
|
||||
## Setup Environment Variables
|
||||
|
||||
Copy the example docker environment file to `.env`:
|
||||
|
||||
```sh
|
||||
cp example.env .env
|
||||
```
|
||||
|
||||
Edit `.env` and set variables according to your needs. See [environment variables](04-env-variables.md) for detailed descriptions of all available variables.
|
||||
|
||||
## Storing Generated YAML Files
|
||||
|
||||
YAML files generated by `docker compose config` can be stored in a directory for version control and management:
|
||||
|
||||
```shell
|
||||
mkdir ~/gitops
|
||||
```
|
||||
|
||||
You can make this directory into a private git repository to track changes to your configuration. This is especially useful for managing multiple environments or projects.
|
||||
|
||||
Alternatively, you can directly use `docker compose up` to start containers without storing intermediate YAML files.
|
||||
|
||||
## Example 1: Frappe without Proxy (Direct Access)
|
||||
|
||||
Setup Frappe with containerized MariaDB and Redis, exposing the application directly on port `:8080` without a reverse proxy.
|
||||
|
||||
**Requirements:**
|
||||
|
||||
- Set `DB_PASSWORD` in `.env` (or use default `123`)
|
||||
- No external database or Redis needed
|
||||
|
||||
```sh
|
||||
# Generate YAML
|
||||
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
|
||||
|
||||
# Start containers
|
||||
docker compose --project-name <project-name> -f ~/gitops/docker-compose.yml up -d
|
||||
```
|
||||
|
||||
## Example 2: ERPNext with External Database and Redis
|
||||
|
||||
Setup ERPNext using external MariaDB and Redis instances with Traefik HTTP proxy.
|
||||
|
||||
**Requirements:**
|
||||
|
||||
- Set `DB_HOST`, `DB_PORT`, `REDIS_CACHE`, and `REDIS_QUEUE` in `.env`
|
||||
- External database and Redis must be accessible
|
||||
|
||||
```sh
|
||||
# Generate YAML
|
||||
docker compose -f compose.yaml \
|
||||
-f overrides/compose.proxy.yaml \
|
||||
config > ~/gitops/docker-compose.yml
|
||||
|
||||
# Start containers
|
||||
docker compose --project-name <project-name> -f ~/gitops/docker-compose.yml up -d
|
||||
```
|
||||
|
||||
## Example 3: Production Setup with HTTPS
|
||||
|
||||
Setup Frappe/ERPNext using containerized MariaDB and Redis with Let's Encrypt SSL certificates via Traefik.
|
||||
|
||||
**Requirements:**
|
||||
|
||||
- Set `LETSENCRYPT_EMAIL` and `SITES_RULE` environment variables
|
||||
- DNS must point to your server IP
|
||||
|
||||
```sh
|
||||
# Generate YAML
|
||||
docker compose -f compose.yaml \
|
||||
-f overrides/compose.mariadb.yaml \
|
||||
-f overrides/compose.redis.yaml \
|
||||
-f overrides/compose.https.yaml \
|
||||
config > ~/gitops/docker-compose.yml
|
||||
|
||||
# Start containers
|
||||
docker compose --project-name <project-name> -f ~/gitops/docker-compose.yml up -d
|
||||
```
|
||||
|
||||
> **Note:** Ensure your `SITES_RULE` variable is properly formatted. See [environment variables](04-env-variables.md) for the correct format.
|
||||
|
||||
## Create First Site
|
||||
|
||||
After starting containers, create your first site. Refer to [site operations](../04-operations/01-site-operations.md#setup-new-site) for detailed instructions.
|
||||
|
||||
## Updating Images
|
||||
|
||||
To update to newer versions of Frappe or ERPNext:
|
||||
|
||||
```sh
|
||||
# 1. Update environment variables in .env
|
||||
nano .env
|
||||
# Edit ERPNEXT_VERSION and FRAPPE_VERSION as needed
|
||||
|
||||
# 2. Regenerate compose file with new versions
|
||||
docker compose --env-file .env \
|
||||
-f compose.yaml \
|
||||
# ... your other overrides
|
||||
config > ~/gitops/docker-compose.yml
|
||||
|
||||
# 3. Pull new images
|
||||
docker compose --project-name <project-name> -f ~/gitops/docker-compose.yml pull
|
||||
|
||||
# 4. Stop containers
|
||||
docker compose --project-name <project-name> -f ~/gitops/docker-compose.yml down
|
||||
|
||||
# 5. Restart containers
|
||||
docker compose --project-name <project-name> -f ~/gitops/docker-compose.yml up -d
|
||||
```
|
||||
|
||||
> **Note:**
|
||||
>
|
||||
> - Pull and stop container commands can be skipped if immutable image tags are used
|
||||
> - `docker compose up -d` will pull new immutable tags if not found
|
||||
|
||||
To migrate sites after updating, refer to [site operations](../04-operations/01-site-operations.md#migrate-site).
|
||||
|
||||
---
|
||||
|
||||
**Back:** [Start Setup →](03-start-setup.md)
|
||||
|
||||
**Next:** [Single Server Example →](07-single-server-example.md)
|
||||
|
|
@ -1,4 +1,10 @@
|
|||
### Single Server Example
|
||||
---
|
||||
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.
|
||||
|
||||
In this use case we have a single server with a static IP attached to it. It can be used in scenarios where one powerful VM has multiple benches and applications or one entry level VM with single site. For single bench, single site setup follow only up to the point where first bench and first site is added. If you choose this setup you can only scale vertically. If you need to scale horizontally you'll need to backup the sites and restore them on to cluster setup.
|
||||
|
||||
|
|
@ -140,7 +146,7 @@ cp example.env ~/gitops/erpnext-one.env
|
|||
sed -i 's/DB_PASSWORD=123/DB_PASSWORD=changeit/g' ~/gitops/erpnext-one.env
|
||||
sed -i 's/DB_HOST=/DB_HOST=mariadb-database/g' ~/gitops/erpnext-one.env
|
||||
sed -i 's/DB_PORT=/DB_PORT=3306/g' ~/gitops/erpnext-one.env
|
||||
sed -i 's/SITES=`erp.example.com`/SITES=\`one.example.com\`,\`two.example.com\`/g' ~/gitops/erpnext-one.env
|
||||
sed -i 's/SITES_RULE=Host(`erp.example.com`)/SITES_RULE=Host(`one.example.com`) || Host(`two.example.com`)/g' ~/gitops/erpnext-one.env
|
||||
echo 'ROUTER=erpnext-one' >> ~/gitops/erpnext-one.env
|
||||
echo "BENCH_NETWORK=erpnext-one" >> ~/gitops/erpnext-one.env
|
||||
```
|
||||
|
|
@ -202,7 +208,7 @@ sed -i 's/DB_PASSWORD=123/DB_PASSWORD=changeit/g' ~/gitops/erpnext-two.env
|
|||
sed -i 's/DB_HOST=/DB_HOST=mariadb-database/g' ~/gitops/erpnext-two.env
|
||||
sed -i 's/DB_PORT=/DB_PORT=3306/g' ~/gitops/erpnext-two.env
|
||||
echo "ROUTER=erpnext-two" >> ~/gitops/erpnext-two.env
|
||||
echo "SITES=\`three.example.com\`,\`four.example.com\`" >> ~/gitops/erpnext-two.env
|
||||
echo 'SITES_RULE=Host(`three.example.com`) || Host(`four.example.com`)' >> ~/gitops/erpnext-two.env
|
||||
echo "BENCH_NETWORK=erpnext-two" >> ~/gitops/erpnext-two.env
|
||||
```
|
||||
|
||||
|
|
@ -251,7 +257,7 @@ Create environment file
|
|||
|
||||
```shell
|
||||
echo "ROUTER=custom-one-example" > ~/gitops/custom-one-example.env
|
||||
echo "SITES=\`custom-one.example.com\`" >> ~/gitops/custom-one-example.env
|
||||
echo 'SITES_RULE=Host(`custom-one.example.com`)' >> ~/gitops/custom-one-example.env
|
||||
echo "BASE_SITE=one.example.com" >> ~/gitops/custom-one-example.env
|
||||
echo "BENCH_NETWORK=erpnext-one" >> ~/gitops/custom-one-example.env
|
||||
```
|
||||
|
|
@ -260,7 +266,7 @@ Note:
|
|||
|
||||
- Change the file name from `custom-one-example.env` to a logical one.
|
||||
- Change `ROUTER` variable from `custom-one.example.com` to the one being added.
|
||||
- Change `SITES` variable from `custom-one.example.com` to the one being added. You can add multiple sites quoted in backtick (`) and separated by commas.
|
||||
- Change `SITES_RULE` variable to the one being added. You can add multiple sites with `Host(...) || Host(...)`.
|
||||
- Change `BASE_SITE` variable from `one.example.com` to the one which is being pointed to.
|
||||
- Change `BENCH_NETWORK` variable from `erpnext-one` to the one which was created with the bench.
|
||||
|
||||
|
|
@ -285,4 +291,8 @@ docker compose --project-name custom-one-example -f ~/gitops/custom-one-example.
|
|||
|
||||
### Site operations
|
||||
|
||||
Refer: [site operations](./site-operations.md)
|
||||
Refer: [site operations](../04-operations/01-site-operations.md)
|
||||
|
||||
---
|
||||
|
||||
**Back:** [Setup Examples →](06-setup-examples.md)
|
||||
173
docs/02-setup/08-single-server-nginxproxy-example.md
Normal file
173
docs/02-setup/08-single-server-nginxproxy-example.md
Normal 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
3
docs/02-setup/index.md
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
---
|
||||
title: Setup
|
||||
---
|
||||
57
docs/03-production/01-tls-ssl-setup.md
Normal file
57
docs/03-production/01-tls-ssl-setup.md
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
---
|
||||
title: TLS/SSL Setup Overview
|
||||
---
|
||||
|
||||
# TLS/SSL Setup Overview
|
||||
|
||||
Frappe Docker supports multiple TLS/SSL approaches. Choose the one that matches your routing needs and where you want the proxy to run.
|
||||
|
||||
## Options
|
||||
|
||||
### Traefik (built-in HTTPS)
|
||||
|
||||
- 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)
|
||||
|
||||
#### Traefik deployment models
|
||||
|
||||
- **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
|
||||
|
||||
- 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
|
||||
|
|
@ -1,3 +1,7 @@
|
|||
---
|
||||
title: Backup Strategy
|
||||
---
|
||||
|
||||
Create backup service or stack.
|
||||
|
||||
```yaml
|
||||
|
|
@ -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
|
||||
86
docs/03-production/04-nginx-proxy-acme-companion.md
Normal file
86
docs/03-production/04-nginx-proxy-acme-companion.md
Normal 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).
|
||||
48
docs/03-production/05-caddy-https.md
Normal file
48
docs/03-production/05-caddy-https.md
Normal 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).
|
||||
147
docs/03-production/06-automated-builds-and-deployment.md
Normal file
147
docs/03-production/06-automated-builds-and-deployment.md
Normal 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)
|
||||
3
docs/03-production/index.md
Normal file
3
docs/03-production/index.md
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
---
|
||||
title: Production
|
||||
---
|
||||
|
|
@ -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](images-and-compose-files.md#overrides)) 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
|
||||
3
docs/04-operations/index.md
Normal file
3
docs/04-operations/index.md
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
---
|
||||
title: Operations
|
||||
---
|
||||
|
|
@ -1,3 +1,7 @@
|
|||
---
|
||||
title: Getting Started
|
||||
---
|
||||
|
||||
# Getting Started
|
||||
|
||||
## Prerequisites
|
||||
|
|
@ -14,9 +18,9 @@ It is recommended you allocate at least 4GB of RAM to docker:
|
|||
- [Instructions for macOS](https://docs.docker.com/desktop/settings/mac/#advanced)
|
||||
|
||||
Here is a screenshot showing the relevant setting in the Help Manual
|
||||

|
||||

|
||||
Here is a screenshot showing the settings in Docker Desktop on Mac
|
||||

|
||||

|
||||
|
||||
## Bootstrap Containers for development
|
||||
|
||||
|
|
@ -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:
|
||||
|
|
@ -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
|
||||
|
|
@ -1,3 +1,7 @@
|
|||
---
|
||||
title: Local Services
|
||||
---
|
||||
|
||||
Add following to frappe container from the `.devcontainer/docker-compose.yml`:
|
||||
|
||||
```yaml
|
||||
254
docs/05-development/04-alternate-setup.md
Normal file
254
docs/05-development/04-alternate-setup.md
Normal 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`
|
||||
3
docs/05-development/index.md
Normal file
3
docs/05-development/index.md
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
---
|
||||
title: Development
|
||||
---
|
||||
|
|
@ -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`.
|
||||
|
|
@ -6,7 +10,7 @@ Now you need to specify command and environment variables for following containe
|
|||
|
||||
### Frontend
|
||||
|
||||
For `frontend` service to act as static assets frontend and reverse proxy, you need to pass `nginx-entrypoint.sh` as container `command` and `BACKEND` and `SOCKETIO` environment variables pointing `{host}:{port}` for gunicorn and websocket services. Check [environment variables](environment-variables.md)
|
||||
For `frontend` service to act as static assets frontend and reverse proxy, you need to pass `nginx-entrypoint.sh` as container `command` and `BACKEND` and `SOCKETIO` environment variables pointing `{host}:{port}` for gunicorn and websocket services. Check [environment variables](../02-setup/04-env-variables.md)
|
||||
|
||||
Now you only need to mount the `sites` volume at location `/home/frappe/frappe-bench/sites`. No need for `assets` volume and asset population script or steps.
|
||||
|
||||
|
|
@ -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.
|
||||
83
docs/06-migration/02-traefik-v3-migration.md
Normal file
83
docs/06-migration/02-traefik-v3-migration.md
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
---
|
||||
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.
|
||||
|
||||
> Note: The Traefik v2 -> v3 migration is complete. The provided overrides no longer set `core.defaultRuleSyntax` or per-router `ruleSyntax` labels, because v3 is the default rule syntax.
|
||||
> Note: If you have a system that must continue to run on v2 despite EOL, you can pin v2 rule syntax with `--core.defaultRuleSyntax=v2` in your Traefik service.
|
||||
|
||||
### Before you start
|
||||
|
||||
Before migrating anything, it is always recommended to create a backup. Better safe than sorry. In particular, compose and .env should be backed up.
|
||||
|
||||
### Quick upgrade summary
|
||||
|
||||
1. Pull the updated repo
|
||||
2. Update env variables especially the updated `SITES` to `SITES_RULE`
|
||||
3. Regenerate the compose config and restart the stack
|
||||
|
||||
#### Multiple hostnames
|
||||
|
||||
v2 allowed comma-separated host lists inside `Host(...)`. In v3 Traefik uses logical OR.
|
||||
|
||||
**Before (v2):**
|
||||
|
||||
```
|
||||
Host(`a.example.com`,`b.example.com`)
|
||||
```
|
||||
|
||||
**After (v3):**
|
||||
|
||||
```
|
||||
Host(`a.example.com`) || Host(`b.example.com`)
|
||||
```
|
||||
|
||||
### Step 1: Replace `SITES` with `SITES_RULE`
|
||||
|
||||
All Traefik routing for HTTPS and multi-bench setups now uses `SITES_RULE`, which is a full v3 rule expression.
|
||||
|
||||
**Single site:**
|
||||
|
||||
```
|
||||
SITES_RULE=Host(`erp.example.com`)
|
||||
```
|
||||
|
||||
**Multiple sites:**
|
||||
|
||||
```
|
||||
SITES_RULE=Host(`a.example.com`) || Host(`b.example.com`)
|
||||
```
|
||||
|
||||
### Step 2: Regenerate and start your compose config
|
||||
|
||||
Example for HTTPS:
|
||||
|
||||
```sh
|
||||
docker compose --env-file .env \
|
||||
-f compose.yaml \
|
||||
-f overrides/compose.mariadb.yaml \
|
||||
-f overrides/compose.redis.yaml \
|
||||
-f overrides/compose.https.yaml \
|
||||
config > ~/gitops/docker-compose.yml
|
||||
```
|
||||
|
||||
```sh
|
||||
docker compose --project-name <project-name> -f ~/gitops/docker-compose.yml up -d
|
||||
```
|
||||
|
||||
See [Single Server Example](../02-setup/07-single-server-example.md)
|
||||
|
||||
### Step 3: Verify Traefik
|
||||
|
||||
After restarting, Traefik will be used in the new supported version 3.6 and the same URLs will be used for the instances when making adjustments. After that, the pages should be accessible as before via the proxy and, if using HTTPS, via HTTPS.
|
||||
|
||||
### Rollback
|
||||
|
||||
If you need to rollback:
|
||||
|
||||
1. Revert Traefik image to `v2.11`
|
||||
2. Restore the old `SITES` variable format and v2 rules
|
||||
3. Regenerate the compose config and restart
|
||||
49
docs/06-migration/03-postgres-major-version-upgrade.md
Normal file
49
docs/06-migration/03-postgres-major-version-upgrade.md
Normal 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
|
||||
```
|
||||
3
docs/06-migration/index.md
Normal file
3
docs/06-migration/index.md
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
---
|
||||
title: Migration
|
||||
---
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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
|
||||
```
|
||||
10
docs/07-troubleshooting/03-arm64-apple-silicon.md
Normal file
10
docs/07-troubleshooting/03-arm64-apple-silicon.md
Normal 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.
|
||||
3
docs/07-troubleshooting/index.md
Normal file
3
docs/07-troubleshooting/index.md
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
---
|
||||
title: Troubleshooting
|
||||
---
|
||||
|
|
@ -1,3 +1,7 @@
|
|||
---
|
||||
title: Build Version 10
|
||||
---
|
||||
|
||||
Clone the version-10 branch of this repo
|
||||
|
||||
```shell
|
||||
45
docs/08-reference/02-configuring-vitepress.md
Normal file
45
docs/08-reference/02-configuring-vitepress.md
Normal 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.
|
||||
161
docs/08-reference/03-fork-management.md
Normal file
161
docs/08-reference/03-fork-management.md
Normal 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"
|
||||
```
|
||||
163
docs/08-reference/04-framework-comparisons.md
Normal file
163
docs/08-reference/04-framework-comparisons.md
Normal 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.
|
||||
18
docs/08-reference/05-external-links.md
Normal file
18
docs/08-reference/05-external-links.md
Normal 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
|
||||
312
docs/08-reference/06-github-actions-image-workflows.md
Normal file
312
docs/08-reference/06-github-actions-image-workflows.md
Normal 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`
|
||||
62
docs/08-reference/07-how-assets-are-handled.md
Normal file
62
docs/08-reference/07-how-assets-are-handled.md
Normal 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.
|
||||
3
docs/08-reference/index.md
Normal file
3
docs/08-reference/index.md
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
---
|
||||
title: References
|
||||
---
|
||||
64
docs/09-concepts/01-custom-app.md
Normal file
64
docs/09-concepts/01-custom-app.md
Normal 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
|
||||
```
|
||||
62
docs/09-concepts/02-docker-bind-mounts.md
Normal file
62
docs/09-concepts/02-docker-bind-mounts.md
Normal 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.
|
||||
3
docs/09-concepts/index.md
Normal file
3
docs/09-concepts/index.md
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
---
|
||||
title: Concepts
|
||||
---
|
||||
|
|
@ -1,125 +0,0 @@
|
|||
## Prerequisites
|
||||
|
||||
- podman
|
||||
- podman-compose
|
||||
- docker-compose
|
||||
|
||||
Podman (the POD MANager) is a tool for managing containers and images, volumes mounted into those containers, and pods made from groups of containers. It is available on the official repositories of many Linux distributions.
|
||||
|
||||
## Step 1
|
||||
|
||||
- Clone this repository and change the current directory to the downloaded folder
|
||||
```cmd
|
||||
git clone https://github.com/frappe/frappe_docker
|
||||
cd frappe_docker
|
||||
```
|
||||
|
||||
## Step 2
|
||||
|
||||
- Create `apps.json` file with custom apps listed in it
|
||||
```json
|
||||
[
|
||||
{
|
||||
"url": "https://github.com/frappe/erpnext",
|
||||
"branch": "version-15"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/frappe/hrms",
|
||||
"branch": "version-15"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/frappe/helpdesk",
|
||||
"branch": "main"
|
||||
}
|
||||
]
|
||||
```
|
||||
Check the syntax of the file using `jq empty apps.json`
|
||||
### Generate base64 string from JSON file:
|
||||
`cmd export APPS_JSON_BASE64=$(base64 -w 0 apps.json)`
|
||||
|
||||
## Step 3
|
||||
|
||||
- Building the custom image using podman
|
||||
|
||||
```ruby
|
||||
podman build \
|
||||
--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 \
|
||||
--file=images/layered/Containerfile .
|
||||
```
|
||||
|
||||
### Note
|
||||
|
||||
- Make sure to use the same tag when you export a variable on the next step
|
||||
|
||||
## Step 4
|
||||
|
||||
- Using the image
|
||||
- Export environment variables with image name, tag and pull_policy
|
||||
```ruby
|
||||
export CUSTOM_IMAGE=custom
|
||||
export CUSTOM_TAG=15
|
||||
export PULL_POLICY=never
|
||||
```
|
||||
- Configuration of parameters used when starting the containers
|
||||
- create `.env` file copying from example.env (Read more on setting up environment variables [here](https://github.com/frappe/frappe_docker/blob/main/docs/environment-variables.md)
|
||||
|
||||
## Final step
|
||||
|
||||
- Creating a compose file
|
||||
- ```ruby
|
||||
podman compose -f compose.yaml \
|
||||
-f overrides/compose.mariadb.yaml \
|
||||
-f overrides/compose.redis.yaml \
|
||||
-f overrides/compose.noproxy.yaml \
|
||||
config > ./docker-compose.yml
|
||||
```
|
||||
### NOTE
|
||||
- podman compose is just a wrapper, it uses docker-compose if it is available or podman-compose if not. podman-compose have an issue reading .env files ([Issue](https://github.com/containers/podman-compose/issues/475)) and might create an issue when running the containers.
|
||||
- Creating pod and starting the containers
|
||||
- `podman-compose --in-pod=1 --project-name erpnext -f ./docker-compose.yml up -d`
|
||||
|
||||
## Creating sites and installing apps
|
||||
|
||||
- You can create sites from the backend container
|
||||
- `podman exec -ti erpnext_backend_1 /bin/bash`
|
||||
- `bench new-site myerp.net --mariadb-root-password 123456 --admin-password 123123`
|
||||
- `bench --site myerp.net install-app erpnext`
|
||||
|
||||
## Autostart pod
|
||||
|
||||
- Systemd is the best option on autostart pods when the system boots. Create a unit file in either `/etc/systemd/system` [for root user] or `~/.config/systemd/user` [for non-root user]
|
||||
|
||||
```ruby
|
||||
[Unit]
|
||||
Description=Podman system daemon service
|
||||
After=network-online.target
|
||||
|
||||
[Service]
|
||||
#User=
|
||||
#Group=
|
||||
Type=oneshot
|
||||
ExecStart=podman pod start POD_NAME
|
||||
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
|
||||
```
|
||||
|
||||
**Note:** Replace POD_NAME with a created pod name while creating a pod. This is a basic systemd unit file to autostart pod, but multiple options can be used, refer to the man page for [systemd](https://man7.org/linux/man-pages/man1/init.1.html). For better management of containers, [Quadlet](https://docs.podman.io/en/v4.4/markdown/podman-systemd.unit.5.html) is the best option for ease of updating and tracing issues on each container.
|
||||
|
||||
## Troubleshoot
|
||||
|
||||
- If there is a network issue while building the image, you need to remove caches and restart again
|
||||
|
||||
- `podman system reset`
|
||||
- `sudo rm -rf ~/.local/share/containers/ /var/lib/container ~/.caches/containers`
|
||||
|
||||
- Database issue when restarting the container
|
||||
- Execute the following commands from **backend** container
|
||||
- `mysql -uroot -padmin -hdb` (Note: put your db password in place of _admin_).
|
||||
- `SELECT User, Host FROM mysql.user;`
|
||||
- Change the IP address to %, e.g. `RENAME USER '_5e5899d8398b5f7b'@'172.18.0.7' TO '_5e5899d8398b5f7b'@'%'`
|
||||
|
|
@ -1,156 +0,0 @@
|
|||
### Load custom apps through apps.json file
|
||||
|
||||
Base64 encoded string of `apps.json` file needs to be passed in as build arg environment variable.
|
||||
|
||||
Create the following `apps.json` file:
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"url": "https://github.com/frappe/erpnext",
|
||||
"branch": "version-15"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/frappe/payments",
|
||||
"branch": "version-15"
|
||||
},
|
||||
{
|
||||
"url": "https://{{ PAT }}@git.example.com/project/repository.git",
|
||||
"branch": "main"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
Note:
|
||||
|
||||
- The `url` needs to be http(s) git url with personal access tokens without username eg:- `http://{{PAT}}@github.com/project/repository.git` in case of private repo.
|
||||
- Add dependencies manually in `apps.json` e.g. add `erpnext` if you are installing `hrms`.
|
||||
- Use fork repo or branch for ERPNext in case you need to use your fork or test a PR.
|
||||
|
||||
Generate base64 string from json file:
|
||||
|
||||
```shell
|
||||
export APPS_JSON_BASE64=$(base64 -w 0 /path/to/apps.json)
|
||||
```
|
||||
|
||||
Test the Previous Step: Decode the Base64-encoded Environment Variable
|
||||
|
||||
To verify the previous step, decode the `APPS_JSON_BASE64` environment variable (which is Base64-encoded) into a JSON file. Follow the steps below:
|
||||
|
||||
1. Use the following command to decode and save the output into a JSON file named apps-test-output.json:
|
||||
|
||||
```shell
|
||||
echo -n ${APPS_JSON_BASE64} | base64 -d > apps-test-output.json
|
||||
```
|
||||
|
||||
2. Open the apps-test-output.json file to review the JSON output and ensure that the content is correct.
|
||||
|
||||
### Clone frappe_docker and switch directory
|
||||
|
||||
```shell
|
||||
git clone https://github.com/frappe/frappe_docker
|
||||
cd frappe_docker
|
||||
```
|
||||
|
||||
### Configure build
|
||||
|
||||
Common build args.
|
||||
|
||||
- `FRAPPE_PATH`, customize the source repo for frappe framework. Defaults to `https://github.com/frappe/frappe`
|
||||
- `FRAPPE_BRANCH`, customize the source repo branch for frappe framework. Defaults to `version-15`.
|
||||
- `APPS_JSON_BASE64`, correct base64 encoded JSON string generated from `apps.json` file.
|
||||
|
||||
Notes
|
||||
|
||||
- Use `buildah` or `docker` as per your setup.
|
||||
- Make sure `APPS_JSON_BASE64` variable has correct base64 encoded JSON string. It is consumed as build arg, base64 encoding ensures it to be friendly with environment variables. Use `jq empty apps.json` to validate `apps.json` file.
|
||||
- Make sure the `--tag` is valid image name that will be pushed to registry. See section [below](#use-images) for remarks about its use.
|
||||
- `.git` directories for all apps are removed from the image.
|
||||
|
||||
### Quick build image
|
||||
|
||||
This method uses pre-built `frappe/base:${FRAPPE_BRANCH}` and `frappe/build:${FRAPPE_BRANCH}` image layers which come with required Python and NodeJS runtime. It speeds up the build time.
|
||||
|
||||
It uses `images/layered/Containerfile`.
|
||||
|
||||
```shell
|
||||
docker build \
|
||||
--build-arg=FRAPPE_PATH=https://github.com/frappe/frappe \
|
||||
--build-arg=FRAPPE_BRANCH=version-15 \
|
||||
--build-arg=APPS_JSON_BASE64=$APPS_JSON_BASE64 \
|
||||
--tag=ghcr.io/user/repo/custom:1.0.0 \
|
||||
--file=images/layered/Containerfile .
|
||||
```
|
||||
|
||||
### Custom build image
|
||||
|
||||
This method builds the base and build layer every time, it allows to customize Python and NodeJS runtime versions. It takes more time to build.
|
||||
|
||||
It uses `images/custom/Containerfile`.
|
||||
|
||||
```shell
|
||||
docker build \
|
||||
--build-arg=FRAPPE_PATH=https://github.com/frappe/frappe \
|
||||
--build-arg=FRAPPE_BRANCH=version-15 \
|
||||
--build-arg=PYTHON_VERSION=3.11.9 \
|
||||
--build-arg=NODE_VERSION=20.19.2 \
|
||||
--build-arg=APPS_JSON_BASE64=$APPS_JSON_BASE64 \
|
||||
--tag=ghcr.io/user/repo/custom:1.0.0 \
|
||||
--file=images/custom/Containerfile .
|
||||
```
|
||||
|
||||
Custom build args,
|
||||
|
||||
- `PYTHON_VERSION`, use the specified python version for base image. Default is `3.11.6`.
|
||||
- `NODE_VERSION`, use the specified nodejs version, Default `20.19.2`.
|
||||
- `DEBIAN_BASE` use the base Debian version, defaults to `bookworm`.
|
||||
- `WKHTMLTOPDF_VERSION`, use the specified qt patched `wkhtmltopdf` version. Default is `0.12.6.1-3`.
|
||||
- `WKHTMLTOPDF_DISTRO`, use the specified distro for debian package. Default is `bookworm`.
|
||||
|
||||
### Push image to use in yaml files
|
||||
|
||||
Login to `docker` or `buildah`
|
||||
|
||||
```shell
|
||||
docker login
|
||||
```
|
||||
|
||||
Push image
|
||||
|
||||
```shell
|
||||
docker push ghcr.io/user/repo/custom:1.0.0
|
||||
```
|
||||
|
||||
### Use Images
|
||||
|
||||
In the [compose.yaml](../compose.yaml), you can set the image name and tag through environment variables, making it easier to customize.
|
||||
|
||||
```yaml
|
||||
x-customizable-image: &customizable_image
|
||||
image: ${CUSTOM_IMAGE:-frappe/erpnext}:${CUSTOM_TAG:-${ERPNEXT_VERSION:?No ERPNext version or tag set}}
|
||||
pull_policy: ${PULL_POLICY:-always}
|
||||
```
|
||||
|
||||
The environment variables can be set in the shell or in the .env file as [setup-options.md](setup-options.md) describes.
|
||||
|
||||
- `CUSTOM_IMAGE`: The name of your custom image. Defaults to `frappe/erpnext` if not set.
|
||||
- `CUSTOM_TAG`: The tag for your custom image. Must be set if `CUSTOM_IMAGE` is used. Defaults to the value of `ERPNEXT_VERSION` if not set.
|
||||
- `PULL_POLICY`: The Docker pull policy. Defaults to `always`. Recommended set to `never` for local images, so prevent `docker` from trying to download the image when it has been built locally.
|
||||
- `HTTP_PUBLISH_PORT`: The port to publish through no SSL channel. Default depending on deployment, it may be `80` if SSL activated or `8080` if not.
|
||||
- `HTTPS_PUBLISH_PORT`: The secure port to publish using SSL. Default is `443`.
|
||||
|
||||
Make sure the image name is correct before pushing to the registry. After the images are pushed, you can pull them to servers to be deployed. If the registry is private, additional auth is needed.
|
||||
|
||||
#### Example
|
||||
|
||||
If you built an image with the tag `ghcr.io/user/repo/custom:1.0.0`, you would set the environment variables as follows:
|
||||
|
||||
```bash
|
||||
export CUSTOM_IMAGE='ghcr.io/user/repo/custom'
|
||||
export CUSTOM_TAG='1.0.0'
|
||||
docker compose -f compose.yaml \
|
||||
-f overrides/compose.mariadb.yaml \
|
||||
-f overrides/compose.redis.yaml \
|
||||
-f overrides/compose.https.yaml \
|
||||
config > ~/gitops/docker-compose.yaml
|
||||
```
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
## Environment Variables
|
||||
|
||||
All of the commands are directly passed to container as per type of service. Only environment variables used in image are for `nginx-entrypoint.sh` command. They are as follows:
|
||||
|
||||
- `BACKEND`: Set to `{host}:{port}`, defaults to `0.0.0.0:8000`
|
||||
- `SOCKETIO`: Set to `{host}:{port}`, defaults to `0.0.0.0:9000`
|
||||
- `UPSTREAM_REAL_IP_ADDRESS`: Set Nginx config for [ngx_http_realip_module#set_real_ip_from](http://nginx.org/en/docs/http/ngx_http_realip_module.html#set_real_ip_from), defaults to `127.0.0.1`
|
||||
- `UPSTREAM_REAL_IP_HEADER`: Set Nginx config for [ngx_http_realip_module#real_ip_header](http://nginx.org/en/docs/http/ngx_http_realip_module.html#real_ip_header), defaults to `X-Forwarded-For`
|
||||
- `UPSTREAM_REAL_IP_RECURSIVE`: Set Nginx config for [ngx_http_realip_module#real_ip_recursive](http://nginx.org/en/docs/http/ngx_http_realip_module.html#real_ip_recursive) Set defaults to `off`
|
||||
- `FRAPPE_SITE_NAME_HEADER`: Set proxy header `X-Frappe-Site-Name` and serve site named in the header, defaults to `$host`, i.e. find site name from host header. More details [below](#frappe_site_name_header)
|
||||
- `PROXY_READ_TIMEOUT`: Upstream gunicorn service timeout, defaults to `120`
|
||||
- `CLIENT_MAX_BODY_SIZE`: Max body size for uploads, defaults to `50m`
|
||||
|
||||
To bypass `nginx-entrypoint.sh`, mount desired `/etc/nginx/conf.d/default.conf` and run `nginx -g 'daemon off;'` as container command.
|
||||
|
||||
## Configuration
|
||||
|
||||
We use environment variables to configure our setup. docker-compose uses variables from the `environment:` section of the services defined within and the`.env` file, if present. Variables defined in the `.env` file are referenced via `${VARIABLE_NAME}` within the docker-compose `.yml` file. `example.env` contains a non-exhaustive list of possible configuration variables. To get started, copy `example.env` to `.env`.
|
||||
|
||||
### `FRAPPE_VERSION`
|
||||
|
||||
Frappe framework release. You can find all releases [here](https://github.com/frappe/frappe/releases).
|
||||
|
||||
### `DB_PASSWORD`
|
||||
|
||||
Password for MariaDB (or Postgres) database.
|
||||
|
||||
### `DB_PASSWORD_SECRETS_FILE`
|
||||
|
||||
Path to the db_password.txt file. Set only if you use docker secrets for the database password (use `overrides/compose.mariadb-secrets.yaml`)
|
||||
|
||||
### `DB_HOST`
|
||||
|
||||
Hostname for MariaDB (or Postgres) database. Set only if external service for database is used or the container can not be reached by its service name (db) by other containers.
|
||||
|
||||
### `DB_PORT`
|
||||
|
||||
Port for MariaDB (3306) or Postgres (5432) database. Set only if external service for database is used.
|
||||
|
||||
### `REDIS_CACHE`
|
||||
|
||||
Hostname for redis server to store cache. Set only if external service for redis is used or the container can not be reached by its service name (redis-cache) by other containers.
|
||||
|
||||
### `REDIS_QUEUE`
|
||||
|
||||
Hostname for redis server to store queue data and socketio. Set only if external service for redis is used or the container can not be reached by its service name (redis-queue) by other containers.
|
||||
|
||||
### `ERPNEXT_VERSION`
|
||||
|
||||
ERPNext [release](https://github.com/frappe/erpnext/releases). This variable is required if you use ERPNext override.
|
||||
|
||||
### `LETSENCRYPT_EMAIL`
|
||||
|
||||
Email that used to register https certificate. This one is required only if you use HTTPS override.
|
||||
|
||||
### `FRAPPE_SITE_NAME_HEADER`
|
||||
|
||||
This environment variable is not required. Default value is `$$host` which resolves site by host. For example, if your host is `example.com`, site's name should be `example.com`, or if host is `127.0.0.1` (local debugging), it should be `127.0.0.1` This variable allows to override described behavior. Let's say you create site named `mysite` and do want to access it by `127.0.0.1` host. Than you would set this variable to `mysite`.
|
||||
|
||||
There is other variables not mentioned here. They're somewhat internal and you don't have to worry about them except you want to change main compose file.
|
||||
978
docs/getting-started.md
Normal file
978
docs/getting-started.md
Normal file
|
|
@ -0,0 +1,978 @@
|
|||
---
|
||||
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_
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [How to Use This Guide](#how-to-use-this-guide)
|
||||
- [Understanding Frappe Docker Architecture](#understanding-frappe-docker-architecture)
|
||||
- [Repository Structure](#repository-structure)
|
||||
- [Custom Apps Explained](#custom-apps-explained)
|
||||
- [Development Workflow](#development-workflow)
|
||||
- [Platform Notes](#platform-notes)
|
||||
- [File Locations and Access](#file-locations-and-access)
|
||||
- [Docker Concepts: Bind Mounts](#docker-concepts-bind-mounts)
|
||||
- [Fork Management Best Practices](#fork-management-best-practices)
|
||||
- [Quick Start Examples](#quick-start-examples)
|
||||
- [Framework Comparisons](#framework-comparisons)
|
||||
- [Frappe vs Django](#frappe-vs-django-concepts)
|
||||
- [Resources and References](#resources-and-references)
|
||||
|
||||
---
|
||||
|
||||
## How to Use This Guide
|
||||
|
||||
Walk through the sections sequentially if you're onboarding from scratch, or jump directly using the Table of Contents.
|
||||
|
||||
## Understanding Frappe Docker 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
|
||||
|
||||
### 📁 Core Configuration Files
|
||||
|
||||
> ⚠️ Before deploying, read
|
||||
> **[Choosing a Deployment Method](01-getting-started/01-choosing-a-deployment-method.md)**
|
||||
> to understand the differences between `pwd.yml`, development setup, the Easy Install script and the production setup.
|
||||
|
||||
- **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 [docs/02-setup/01-overview.md](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
|
||||
|
||||
## Custom Apps Explained
|
||||
|
||||
### 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
|
||||
```
|
||||
|
||||
## Development Workflow
|
||||
|
||||
### Quick Test Setup (pwd.yml)
|
||||
|
||||
Perfect for evaluating Frappe Docker without any local setup:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/frappe/frappe_docker
|
||||
cd frappe_docker
|
||||
docker compose -f pwd.yml up -d
|
||||
|
||||
# Monitor site creation (takes ~5 minutes)
|
||||
docker compose -f pwd.yml logs -f create-site
|
||||
|
||||
# Access once "create-site" container exits successfully
|
||||
# Visit http://localhost:8080
|
||||
# Login: Administrator / admin
|
||||
```
|
||||
|
||||
### Full Development Setup
|
||||
|
||||
For active development with hot-reload and debugging:
|
||||
|
||||
1. **Copy devcontainer configuration:**
|
||||
|
||||
```bash
|
||||
cp -R devcontainer-example .devcontainer
|
||||
```
|
||||
|
||||
2. **Open in VSCode with Dev Containers extension** (Remote - Containers)
|
||||
|
||||
- VSCode will detect `.devcontainer` and prompt to reopen in container
|
||||
|
||||
3. **Run automated installer:**
|
||||
|
||||
```bash
|
||||
cd /workspace/development
|
||||
python installer.py
|
||||
# Follow interactive prompts for site name, apps to install, etc.
|
||||
```
|
||||
|
||||
4. **Access development files:**
|
||||
```
|
||||
development/frappe-bench/ # Your live development environment
|
||||
```
|
||||
|
||||
### Development File Locations
|
||||
|
||||
```
|
||||
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
|
||||
```
|
||||
|
||||
### Common Development Commands
|
||||
|
||||
```bash
|
||||
# Inside container
|
||||
bench start # Start development server with hot-reload
|
||||
|
||||
# Database operations
|
||||
bench migrate # Run database migrations
|
||||
bench --site mysite.com migrate # Site-specific migration
|
||||
|
||||
# Frontend builds
|
||||
bench build # Build all app assets
|
||||
bench build --app my_custom_app # Build specific app
|
||||
|
||||
# Code generation
|
||||
bench new-app <app_name> # Create new app
|
||||
bench new-site <site_name> # Create new site
|
||||
|
||||
# App management
|
||||
bench get-app <git_url> # Download app from git
|
||||
bench install-app <app_name> # Install app to current site
|
||||
bench uninstall-app <app_name> # Remove app from site
|
||||
|
||||
# Debugging
|
||||
bench console # Python REPL with Frappe context
|
||||
bench mariadb # Database console
|
||||
```
|
||||
|
||||
## Platform Notes
|
||||
|
||||
### 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.
|
||||
|
||||
## File Locations and Access
|
||||
|
||||
### Accessing Container Files
|
||||
|
||||
```bash
|
||||
# Enter backend container shell
|
||||
docker compose -f pwd.yml exec backend bash
|
||||
|
||||
# Navigate to bench directory
|
||||
cd /home/frappe/frappe-bench/
|
||||
|
||||
# Key directories:
|
||||
/home/frappe/frappe-bench/apps/ # All Frappe apps
|
||||
/home/frappe/frappe-bench/sites/ # Site data and configuration
|
||||
/home/frappe/frappe-bench/logs/ # Application logs
|
||||
/home/frappe/frappe-bench/env/ # Python virtual environment
|
||||
```
|
||||
|
||||
### Copying Files from Containers
|
||||
|
||||
```bash
|
||||
# Copy entire app from container to host
|
||||
docker compose -f pwd.yml cp backend:/home/frappe/frappe-bench/apps/my_app ./local-apps/
|
||||
|
||||
# Copy logs
|
||||
docker compose -f pwd.yml cp backend:/home/frappe/frappe-bench/logs/ ./debug-logs/
|
||||
|
||||
# Copy site files
|
||||
docker compose -f pwd.yml cp backend:/home/frappe/frappe-bench/sites/mysite.com ./backup/
|
||||
```
|
||||
|
||||
### Useful Container Commands
|
||||
|
||||
```bash
|
||||
# List all sites
|
||||
docker compose -f pwd.yml exec backend bench list-sites
|
||||
|
||||
# List installed apps for a site
|
||||
docker compose -f pwd.yml exec backend bench --site mysite.com list-apps
|
||||
|
||||
# View site configuration
|
||||
docker compose -f pwd.yml exec backend cat /home/frappe/frappe-bench/sites/common_site_config.json
|
||||
|
||||
# Check logs in real-time
|
||||
docker compose -f pwd.yml logs -f backend
|
||||
|
||||
# Execute bench command
|
||||
docker compose -f pwd.yml exec backend bench --site mysite.com console
|
||||
|
||||
# Backup site
|
||||
docker compose -f pwd.yml exec backend bench --site mysite.com backup --with-files
|
||||
```
|
||||
|
||||
## Docker Concepts: 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.
|
||||
|
||||
## 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"
|
||||
```
|
||||
|
||||
## Quick Start Examples
|
||||
|
||||
### 1. Quick Test (5 minutes)
|
||||
|
||||
**Goal:** Try Frappe/ERPNext without any local setup
|
||||
|
||||
```bash
|
||||
# Clone and run
|
||||
git clone https://github.com/frappe/frappe_docker
|
||||
cd frappe_docker
|
||||
docker compose -f pwd.yml up -d
|
||||
|
||||
# Monitor setup progress (~5 minutes)
|
||||
docker compose -f pwd.yml logs -f create-site
|
||||
|
||||
# When complete, access:
|
||||
# URL: http://localhost:8080
|
||||
# Username: Administrator
|
||||
# Password: admin
|
||||
|
||||
# Cleanup when done
|
||||
docker compose -f pwd.yml down -v
|
||||
```
|
||||
|
||||
### 2. Development Environment (15 minutes)
|
||||
|
||||
**Goal:** Set up for daily development with hot-reload
|
||||
|
||||
```bash
|
||||
# Copy devcontainer config
|
||||
cp -R devcontainer-example .devcontainer
|
||||
|
||||
# Open in VSCode
|
||||
# 1. Install "Dev Containers" extension
|
||||
# 2. Command Palette (Ctrl+Shift+P) → "Reopen in Container"
|
||||
# 3. Wait for container build (~5 min first time)
|
||||
|
||||
# Inside container terminal:
|
||||
cd /workspace/development
|
||||
python installer.py
|
||||
|
||||
# Follow prompts:
|
||||
# - Site name: development.localhost
|
||||
# - Install ERPNext: Yes
|
||||
# - Version: version-15
|
||||
|
||||
# Start development server
|
||||
cd frappe-bench
|
||||
bench start
|
||||
|
||||
# Access: http://localhost:8000
|
||||
# Edit files in: development/frappe-bench/apps/
|
||||
```
|
||||
|
||||
### 3. Custom App Development (30 minutes)
|
||||
|
||||
**Goal:** Create and develop a custom Frappe application
|
||||
|
||||
```bash
|
||||
# Prerequisite: Complete Example 2 first
|
||||
|
||||
# Inside development container
|
||||
cd /workspace/development/frappe-bench
|
||||
|
||||
# Create new app
|
||||
bench new-app library_management
|
||||
# Follow prompts (title, description, publisher, etc.)
|
||||
|
||||
# Install to site
|
||||
bench --site development.localhost install-app library_management
|
||||
|
||||
# Create DocTypes via web UI:
|
||||
# 1. Go to: http://localhost:8000
|
||||
# 2. Setup → Customize → DocType → New
|
||||
# 3. Create: Book, Author, Borrower, etc.
|
||||
|
||||
# Or create via code:
|
||||
# Edit: apps/library_management/library_management/library_management/doctype/
|
||||
|
||||
# Build and reload
|
||||
bench build --app library_management
|
||||
# Server auto-reloads (bench start watches for changes)
|
||||
```
|
||||
|
||||
### 4. Production Deployment (1 hour)
|
||||
|
||||
**Goal:** Deploy Frappe in production with SSL
|
||||
|
||||
```bash
|
||||
# Follow detailed guide
|
||||
# See: docs/single-server-example.md
|
||||
|
||||
# Quick overview:
|
||||
# 1. Setup server with Docker
|
||||
# 2. Clone frappe_docker
|
||||
# 3. Configure environment variables
|
||||
# 4. Use compose.yaml + production overrides
|
||||
# 5. Setup SSL with Traefik/Let's Encrypt
|
||||
# 6. Deploy and monitor
|
||||
|
||||
# Key files:
|
||||
# - compose.yaml
|
||||
# - compose.mariadb.yaml
|
||||
# - compose.redis.yaml
|
||||
# - compose.proxy.yaml
|
||||
# - compose.https.yaml
|
||||
|
||||
# Deploy command:
|
||||
docker compose \
|
||||
-f compose.yaml \
|
||||
-f overrides/compose.mariadb.yaml \
|
||||
-f overrides/compose.redis.yaml \
|
||||
-f overrides/compose.https.yaml \
|
||||
up -d
|
||||
```
|
||||
|
||||
### 5. Multi-Site Hosting
|
||||
|
||||
**Goal:** Host multiple Frappe sites on one server
|
||||
|
||||
```bash
|
||||
# See: docs/port-based-multi-tenancy.md
|
||||
|
||||
# Quick example:
|
||||
# 1. Create multiple sites in development
|
||||
bench new-site site1.com
|
||||
bench new-site site2.com
|
||||
|
||||
# 2. Configure Nginx/Traefik for routing
|
||||
# 3. Each site gets own database
|
||||
# 4. Shared Redis and application code
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 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.
|
||||
|
||||
---
|
||||
|
||||
## Resources and References
|
||||
|
||||
### 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
|
||||
|
||||
### Key Files in This Repository
|
||||
|
||||
- [`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
|
||||
|
||||
### 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
|
||||
|
||||
### Essential Docker Commands Reference
|
||||
|
||||
```bash
|
||||
# Service Management
|
||||
docker compose up -d # Start all services in background
|
||||
docker compose down # Stop and remove containers
|
||||
docker compose down -v # Stop and remove volumes (data loss!)
|
||||
docker compose restart <service> # Restart specific service
|
||||
docker compose ps # List running services
|
||||
docker compose logs -f <service> # Follow logs for service
|
||||
|
||||
# Container Access
|
||||
docker compose exec <service> bash # Open shell in running container
|
||||
docker compose exec <service> <cmd> # Run command in container
|
||||
docker compose run <service> <cmd> # Run one-off command (creates new container)
|
||||
|
||||
# Debugging
|
||||
docker compose logs --tail=100 <service> # Last 100 log lines
|
||||
docker compose top # Show running processes
|
||||
docker inspect <container_name> # Detailed container info
|
||||
|
||||
# Cleanup
|
||||
docker system prune # Remove unused containers/networks
|
||||
docker volume prune # Remove unused volumes (BE CAREFUL!)
|
||||
docker image prune # Remove unused images
|
||||
```
|
||||
|
||||
### Essential Bench Commands Reference
|
||||
|
||||
```bash
|
||||
# Site Operations
|
||||
bench new-site <site_name> # Create new site
|
||||
bench drop-site <site_name> # Delete site (asks confirmation)
|
||||
bench list-sites # List all sites
|
||||
bench use <site_name> # Set default site
|
||||
|
||||
# App Operations
|
||||
bench get-app <git_url> # Download app from git
|
||||
bench get-app <app_name> # Download from Frappe registry
|
||||
bench install-app <app_name> # Install to default site
|
||||
bench install-app <app_name> --site <site> # Install to specific site
|
||||
bench uninstall-app <app_name> # Uninstall from default site
|
||||
bench list-apps # List installed apps
|
||||
|
||||
# Development
|
||||
bench start # Start development server (hot-reload)
|
||||
bench build # Build frontend assets
|
||||
bench build --app <app_name> # Build specific app
|
||||
bench migrate # Run database migrations
|
||||
bench clear-cache # Clear Redis cache
|
||||
bench clear-website-cache # Clear website route cache
|
||||
|
||||
# Database
|
||||
bench mariadb # Open MariaDB console
|
||||
bench backup # Backup default site
|
||||
bench backup --with-files # Backup with uploaded files
|
||||
bench restore <path> # Restore backup
|
||||
|
||||
# Code Generation
|
||||
bench new-app <app_name> # Create new app
|
||||
bench --site <site> console # Python REPL with Frappe context
|
||||
bench --site <site> execute "<python_code>" # Execute Python code
|
||||
|
||||
# Deployment
|
||||
bench setup production <user> # Setup for production (supervisor, nginx)
|
||||
bench restart # Restart bench processes
|
||||
bench update # Update framework and apps
|
||||
```
|
||||
|
||||
### Troubleshooting Quick Reference
|
||||
|
||||
| Issue | Solution |
|
||||
| ------------------------- | ----------------------------------------------------- |
|
||||
| Port 8080 already in use | Change `PWD_PORT` in `.env` or stop other service |
|
||||
| Container won't start | Check logs: `docker compose logs <service>` |
|
||||
| Site creation fails | Check `create-site` logs, ensure DB is ready |
|
||||
| Can't connect to site | Wait 5 min for initialization, check container health |
|
||||
| Permission errors | Check volume permissions, may need `chown` |
|
||||
| Out of disk space | `docker system prune -a --volumes` (CAREFUL!) |
|
||||
| Python packages missing | `bench pip install <package>` inside container |
|
||||
| Frontend not building | `bench build --force`, check Node.js errors |
|
||||
| Database connection fails | Check `common_site_config.json`, Redis/MariaDB status |
|
||||
|
||||
### Getting Help
|
||||
|
||||
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)
|
||||
|
||||
### Contributing
|
||||
|
||||
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: for coding standards and review expectations.
|
||||
|
||||
---
|
||||
|
||||
_This guide provides a comprehensive overview of Frappe Docker for developers of all backgrounds. For specific use cases or advanced topics, refer to the linked documentation._
|
||||
|
||||
_Last updated: October 2025_
|
||||
26
docs/index.md
Normal file
26
docs/index.md
Normal 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
|
||||
---
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
# Images
|
||||
|
||||
There are 3 images that you can find in `/images` directory:
|
||||
|
||||
- `bench`. It is used for development. [Learn more how to start development](development.md).
|
||||
- `production`.
|
||||
- Multi-purpose Python backend. Runs [Werkzeug server](https://werkzeug.palletsprojects.com/en/2.0.x/) with [gunicorn](https://gunicorn.org), queues (via `bench worker`), or schedule (via `bench schedule`).
|
||||
- Contains JS and CSS assets and routes incoming requests using [nginx](https://www.nginx.com).
|
||||
- Processes realtime websocket requests using [Socket.IO](https://socket.io).
|
||||
- `custom`. It is used to build bench using `apps.json` file set with `--apps_path` during bench initialization. `apps.json` is a json array. e.g. `[{"url":"{{repo_url}}","branch":"{{repo_branch}}"}]`
|
||||
|
||||
Image has everything we need to be able to run all processes that Frappe framework requires (take a look at [Bench Procfile reference](https://frappeframework.com/docs/v14/user/en/bench/resources/bench-procfile)). We follow [Docker best practices](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#decouple-applications) and split these processes to different containers.
|
||||
|
||||
> We use [multi-stage builds](https://docs.docker.com/develop/develop-images/multistage-build/) and [Docker Buildx](https://docs.docker.com/engine/reference/commandline/buildx/) to reuse as much things as possible and make our builds more efficient.
|
||||
|
||||
# Compose files
|
||||
|
||||
After building the images we have to run the containers. The best and simplest way to do this is to use [compose files](https://docs.docker.com/compose/compose-file/).
|
||||
|
||||
We have one main compose file, `compose.yaml`. Services described, networking, volumes are also handled there.
|
||||
|
||||
## Services
|
||||
|
||||
All services are described in `compose.yaml`
|
||||
|
||||
- `configurator`. Updates `common_site_config.json` so Frappe knows how to access db and redis. It is executed on every `docker-compose up` (and exited immediately). Other services start after this container exits successfully.
|
||||
- `backend`. [Werkzeug server](https://werkzeug.palletsprojects.com/en/2.0.x/).
|
||||
- `db`. Optional service that runs [MariaDB](https://mariadb.com) if you also use `overrides/compose.mariadb.yaml` or [Postgres](https://www.postgresql.org) if you also use `overrides/compose.postgres.yaml`.
|
||||
- `redis`. Optional service that runs [Redis](https://redis.io) server with cache, [Socket.IO](https://socket.io) and queues data.
|
||||
- `frontend`. [nginx](https://www.nginx.com) server that serves JS/CSS assets and routes incoming requests.
|
||||
- `proxy`. [Traefik](https://traefik.io/traefik/) proxy. It is here for complicated setups or HTTPS override (with `overrides/compose.https.yaml`).
|
||||
- `websocket`. Node server that runs [Socket.IO](https://socket.io).
|
||||
- `queue-short`, `queue-long`. Python servers that run job queues using [rq](https://python-rq.org).
|
||||
- `scheduler`. Python server that runs tasks on schedule using [schedule](https://schedule.readthedocs.io/en/stable/).
|
||||
|
||||
## Overrides
|
||||
|
||||
We have several [overrides](https://docs.docker.com/compose/extends/):
|
||||
|
||||
- `overrides/compose.proxy.yaml`. Adds traefik proxy to setup.
|
||||
- `overrides/compose.noproxy.yaml`. Publishes `frontend` ports directly without any proxy.
|
||||
- `overrides/compose.https.yaml`. Automatically sets up Let's Encrypt certificate and redirects all requests to directed to http, to https.
|
||||
- `overrides/compose.mariadb.yaml`. Adds `db` service and sets its image to MariaDB.
|
||||
- `overrides/compose.postgres.yaml`. Adds `db` service and sets its image to Postgres. Note that ERPNext currently doesn't support Postgres.
|
||||
- `overrides/compose.redis.yaml`. Adds `redis` service and sets its image to `redis`.
|
||||
|
||||
It is quite simple to run overrides. All we need to do is to specify compose files that should be used by docker-compose. For example, we want ERPNext:
|
||||
|
||||
```bash
|
||||
# Point to main compose file (compose.yaml) and add one more.
|
||||
docker-compose -f compose.yaml -f overrides/compose.redis.yaml config
|
||||
```
|
||||
|
||||
⚠ Make sure to use docker-compose v2 (run `docker-compose -v` to check). If you want to use v1 make sure the correct `$`-signs as they get duplicated by the `config` command!
|
||||
|
||||
That's it! Of course, we also have to setup `.env` before all of that, but that's not the point.
|
||||
|
||||
Check [environment variables](environment-variables.md) for more.
|
||||
19
docs/package.json
Normal file
19
docs/package.json
Normal 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
1759
docs/pnpm-lock.yaml
Normal file
File diff suppressed because it is too large
Load diff
BIN
docs/public/favicon.png
Normal file
BIN
docs/public/favicon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 44 KiB |
BIN
docs/public/frappe-docker.png
Normal file
BIN
docs/public/frappe-docker.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 44 KiB |
|
|
@ -1,131 +0,0 @@
|
|||
# Containerized Production Setup
|
||||
|
||||
Make sure you've cloned this repository and switch to the directory before executing following commands.
|
||||
|
||||
Commands will generate YAML as per the environment for setup.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- [docker](https://docker.com/get-started)
|
||||
- [docker compose v2](https://docs.docker.com/compose/cli-command)
|
||||
|
||||
## Setup Environment Variables
|
||||
|
||||
Copy the example docker environment file to `.env`:
|
||||
|
||||
```sh
|
||||
cp example.env .env
|
||||
```
|
||||
|
||||
Note: To know more about environment variable [read here](./environment-variables.md). Set the necessary variables in the `.env` file.
|
||||
|
||||
## Generate docker-compose.yml for variety of setups
|
||||
|
||||
Notes:
|
||||
|
||||
- Make sure to replace `<project-name>` with the desired name you wish to set for the project.
|
||||
- This setup is not to be used for development. A complete development environment is available [here](../development)
|
||||
|
||||
### Store the yaml files
|
||||
|
||||
YAML files generated by `docker compose config` command can be stored in a directory. We will create a directory called `gitops` in the user's home.
|
||||
|
||||
```shell
|
||||
mkdir ~/gitops
|
||||
```
|
||||
|
||||
You can make the directory into a private git repo which stores the yaml and secrets. It can help in tracking changes.
|
||||
|
||||
Instead of `docker compose config`, you can directly use `docker compose up` to start the containers and skip storing the yamls in `gitops` directory.
|
||||
|
||||
### Setup Frappe without proxy and external MariaDB and Redis
|
||||
|
||||
In this case make sure you've set `DB_HOST`, `DB_PORT`, `REDIS_CACHE` and `REDIS_QUEUE` environment variables or the `configurator` will fail.
|
||||
|
||||
```sh
|
||||
# Generate YAML
|
||||
docker compose -f compose.yaml -f overrides/compose.noproxy.yaml config > ~/gitops/docker-compose.yml
|
||||
|
||||
# Start containers
|
||||
docker compose --project-name <project-name> -f ~/gitops/docker-compose.yml up -d
|
||||
```
|
||||
|
||||
### Setup ERPNext with proxy and external MariaDB and Redis
|
||||
|
||||
In this case make sure you've set `DB_HOST`, `DB_PORT`, `REDIS_CACHE` and `REDIS_QUEUE` environment variables or the `configurator` will fail.
|
||||
|
||||
```sh
|
||||
# Generate YAML
|
||||
docker compose -f compose.yaml \
|
||||
-f overrides/compose.proxy.yaml \
|
||||
config > ~/gitops/docker-compose.yml
|
||||
|
||||
# Start containers
|
||||
docker compose --project-name <project-name> -f ~/gitops/docker-compose.yml up -d
|
||||
```
|
||||
|
||||
### Setup Frappe using containerized MariaDB and Redis with Letsencrypt certificates.
|
||||
|
||||
In this case make sure you've set `LETSENCRYPT_EMAIL` and `SITES` environment variables are set or certificates won't work.
|
||||
|
||||
```sh
|
||||
# Generate YAML
|
||||
docker compose -f compose.yaml \
|
||||
-f overrides/compose.mariadb.yaml \
|
||||
-f overrides/compose.redis.yaml \
|
||||
-f overrides/compose.https.yaml \
|
||||
config > ~/gitops/docker-compose.yml
|
||||
|
||||
# Start containers
|
||||
docker compose --project-name <project-name> -f ~/gitops/docker-compose.yml up -d
|
||||
```
|
||||
|
||||
### Setup ERPNext using containerized MariaDB and Redis with Letsencrypt certificates.
|
||||
|
||||
In this case make sure you've set `LETSENCRYPT_EMAIL` and `SITES` environment variables are set or certificates won't work.
|
||||
|
||||
```sh
|
||||
# Generate YAML
|
||||
docker compose -f compose.yaml \
|
||||
-f overrides/compose.mariadb.yaml \
|
||||
-f overrides/compose.redis.yaml \
|
||||
-f overrides/compose.https.yaml \
|
||||
config > ~/gitops/docker-compose.yml
|
||||
|
||||
# Start containers
|
||||
docker compose --project-name <project-name> -f ~/gitops/docker-compose.yml up -d
|
||||
```
|
||||
|
||||
## Create first site
|
||||
|
||||
After starting containers, the first site needs to be created. Refer [site operations](./site-operations.md#setup-new-site).
|
||||
|
||||
## Updating Images
|
||||
|
||||
Switch to the root of the `frappe_docker` directory before running the following commands:
|
||||
|
||||
```sh
|
||||
# Update environment variables ERPNEXT_VERSION and FRAPPE_VERSION
|
||||
nano .env
|
||||
|
||||
# Pull new images
|
||||
docker compose -f compose.yaml \
|
||||
# ... your other overrides
|
||||
config > ~/gitops/docker-compose.yml
|
||||
|
||||
# Pull images
|
||||
docker compose --project-name <project-name> -f ~/gitops/docker-compose.yml pull
|
||||
|
||||
# Stop containers
|
||||
docker compose --project-name <project-name> -f ~/gitops/docker-compose.yml down
|
||||
|
||||
# Restart containers
|
||||
docker compose --project-name <project-name> -f ~/gitops/docker-compose.yml up -d
|
||||
```
|
||||
|
||||
Note:
|
||||
|
||||
- pull and stop container commands can be skipped if immutable image tags are used
|
||||
- `docker compose up -d` will pull new immutable tags if not found.
|
||||
|
||||
To migrate sites refer [site operations](./site-operations.md#migrate-site)
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
# Accessing ERPNext through https on local deployment
|
||||
|
||||
- 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.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Caddy
|
||||
- Adding a domain name to hosts file
|
||||
|
||||
#### Installation of caddy webserver
|
||||
|
||||
- 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
|
||||
|
||||
```js
|
||||
erp.localdev.net {
|
||||
tls internal
|
||||
|
||||
reverse_proxy localhost:8085 {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- Caddy's root certificate must be added to other computers if computers from different networks access the ERPNext through https.
|
||||
29
example.env
29
example.env
|
|
@ -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.74.0
|
||||
ERPNEXT_VERSION=v16.20.1
|
||||
|
||||
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,7 +63,14 @@ PROXY_READ_TIMEOUT=
|
|||
# Necessary if the upload limit in the frappe application is increased
|
||||
CLIENT_MAX_BODY_SIZE=
|
||||
|
||||
# List of sites for letsencrypt certificates quoted with backtick (`) and separated by comma (,)
|
||||
# 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=`erp.example.com`
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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 \
|
||||
|
|
@ -71,6 +72,13 @@ RUN apt-get update \
|
|||
tk-dev \
|
||||
liblzma-dev \
|
||||
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 \
|
||||
|
|
@ -97,18 +105,18 @@ USER frappe
|
|||
WORKDIR /home/frappe
|
||||
|
||||
# Install Python via pyenv
|
||||
ENV PYTHON_VERSION_V14=3.10.13
|
||||
ENV PYTHON_VERSION=3.11.6
|
||||
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
|
||||
|
|
@ -124,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=20.19.2
|
||||
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} \
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
||||
|
|
@ -37,6 +40,8 @@ RUN useradd -ms /bin/bash frappe \
|
|||
# For healthcheck
|
||||
wait-for-it \
|
||||
jq \
|
||||
# For MIME type detection
|
||||
media-types \
|
||||
# NodeJS
|
||||
&& mkdir -p ${NVM_DIR} \
|
||||
&& curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh | bash \
|
||||
|
|
@ -44,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 \
|
||||
|
|
@ -56,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 \
|
||||
|
|
@ -66,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 \
|
||||
|
|
@ -105,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}\
|
||||
|
|
@ -139,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"]
|
||||
|
|
|
|||
|
|
@ -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"]
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
||||
|
|
@ -34,6 +36,8 @@ RUN useradd -ms /bin/bash frappe \
|
|||
# For healthcheck
|
||||
wait-for-it \
|
||||
jq \
|
||||
# For MIME type detection
|
||||
media-types \
|
||||
# NodeJS
|
||||
&& mkdir -p ${NVM_DIR} \
|
||||
&& curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh | bash \
|
||||
|
|
@ -41,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 \
|
||||
|
|
@ -53,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 \
|
||||
|
|
@ -63,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
|
||||
|
||||
|
|
@ -101,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} \
|
||||
|
|
@ -128,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"]
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
version: "3.3"
|
||||
|
||||
services:
|
||||
custom-domain:
|
||||
image: caddy:2
|
||||
|
|
@ -16,7 +14,7 @@ services:
|
|||
- traefik.http.services.${ROUTER?ROUTER not set}.loadbalancer.server.port=2016
|
||||
- traefik.http.routers.${ROUTER}.service=${ROUTER}
|
||||
- traefik.http.routers.${ROUTER}.entrypoints=http
|
||||
- traefik.http.routers.${ROUTER}.rule=Host(${SITES?SITES not set})
|
||||
- traefik.http.routers.${ROUTER}.rule=${SITES_RULE?SITES_RULE not set}
|
||||
- traefik.http.middlewares.${ROUTER}.headers.customrequestheaders.Host=${BASE_SITE?BASE_SITE not set}
|
||||
- traefik.http.routers.${ROUTER}.middlewares=${ROUTER}
|
||||
networks:
|
||||
|
|
|
|||
|
|
@ -5,10 +5,10 @@ services:
|
|||
- traefik.http.services.frontend.loadbalancer.server.port=8080
|
||||
- traefik.http.routers.frontend-http.entrypoints=websecure
|
||||
- traefik.http.routers.frontend-http.tls.certresolver=main-resolver
|
||||
- traefik.http.routers.frontend-http.rule=Host(${SITES:?List of sites not set})
|
||||
- traefik.http.routers.frontend-http.rule=${SITES_RULE:?SITES_RULE not set}
|
||||
|
||||
proxy:
|
||||
image: traefik:v2.11
|
||||
image: traefik:v3.6
|
||||
restart: unless-stopped
|
||||
command:
|
||||
- --providers.docker=true
|
||||
|
|
|
|||
|
|
@ -3,8 +3,6 @@ services:
|
|||
environment:
|
||||
MYSQL_ROOT_PASSWORD: !reset null
|
||||
MYSQL_ROOT_PASSWORD_FILE: /run/secrets/db_password
|
||||
healthcheck:
|
||||
test: mysqladmin ping -h localhost --password="$(cat /run/secrets/db_password)"
|
||||
secrets:
|
||||
- db_password
|
||||
|
||||
|
|
|
|||
|
|
@ -1,21 +1,21 @@
|
|||
version: "3.3"
|
||||
|
||||
services:
|
||||
database:
|
||||
container_name: mariadb-database
|
||||
image: mariadb:10.6
|
||||
image: mariadb:11.8
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: mysqladmin ping -h localhost --password=${DB_PASSWORD:-changeit}
|
||||
interval: 1s
|
||||
retries: 20
|
||||
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
|
||||
start_period: 5s
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
command:
|
||||
- --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
|
||||
volumes:
|
||||
- db-data:/var/lib/mysql
|
||||
networks:
|
||||
|
|
|
|||
|
|
@ -2,25 +2,27 @@ services:
|
|||
configurator:
|
||||
environment:
|
||||
DB_HOST: db
|
||||
DB_PORT: 3306
|
||||
DB_PORT: "3306"
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
|
||||
db:
|
||||
image: mariadb:10.6
|
||||
image: mariadb:11.8
|
||||
healthcheck:
|
||||
test: mysqladmin ping -h localhost --password=${DB_PASSWORD:-123}
|
||||
interval: 1s
|
||||
retries: 20
|
||||
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
|
||||
start_period: 5s
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
restart: unless-stopped
|
||||
command:
|
||||
- --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
|
||||
volumes:
|
||||
- db-data:/var/lib/mysql
|
||||
|
||||
|
|
|
|||
44
overrides/compose.migrator.yaml
Normal file
44
overrides/compose.migrator.yaml
Normal 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
|
||||
|
|
@ -4,8 +4,8 @@ services:
|
|||
# ${ROUTER}-http to use the middleware to redirect to https
|
||||
- traefik.http.routers.${ROUTER}-http.middlewares=https-redirect
|
||||
# ${ROUTER}-https the actual router using HTTPS
|
||||
# Uses the environment variable SITES
|
||||
- traefik.http.routers.${ROUTER}-https.rule=Host(${SITES?SITES not set})
|
||||
# Uses the environment variable SITES_RULE
|
||||
- traefik.http.routers.${ROUTER}-https.rule=${SITES_RULE?SITES_RULE not set}
|
||||
- traefik.http.routers.${ROUTER}-https.entrypoints=https
|
||||
- traefik.http.routers.${ROUTER}-https.tls=true
|
||||
# Use the service ${ROUTER} with the frontend
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ services:
|
|||
- traefik.http.services.${ROUTER?ROUTER not set}.loadbalancer.server.port=8080
|
||||
- traefik.http.routers.${ROUTER}-http.service=${ROUTER}
|
||||
- traefik.http.routers.${ROUTER}-http.entrypoints=http
|
||||
- traefik.http.routers.${ROUTER}-http.rule=Host(${SITES?SITES not set})
|
||||
- traefik.http.routers.${ROUTER}-http.rule=${SITES_RULE?SITES_RULE not set}
|
||||
configurator:
|
||||
networks:
|
||||
- bench-network
|
||||
|
|
@ -37,12 +37,10 @@ services:
|
|||
redis-cache:
|
||||
networks:
|
||||
- bench-network
|
||||
- mariadb-network
|
||||
|
||||
redis-queue:
|
||||
networks:
|
||||
- bench-network
|
||||
- mariadb-network
|
||||
|
||||
networks:
|
||||
traefik-public:
|
||||
|
|
|
|||
28
overrides/compose.nginxproxy-ssl.yaml
Normal file
28
overrides/compose.nginxproxy-ssl.yaml
Normal 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:
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue