1 Commits

Author SHA1 Message Date
Ankush Menat
0c282cef93 build: add pkg-config dependency 2025-03-15 17:40:57 +05:30
37 changed files with 469 additions and 748 deletions

View File

@@ -20,7 +20,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v5 uses: actions/checkout@v4
- name: Setup QEMU - name: Setup QEMU
uses: docker/setup-qemu-action@v3 uses: docker/setup-qemu-action@v3
@@ -38,7 +38,7 @@ jobs:
run: echo "LATEST_BENCH_RELEASE=$(curl -s 'https://api.github.com/repos/frappe/bench/releases/latest' | jq -r '.tag_name')" >> "$GITHUB_ENV" 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 - name: Build and test
uses: docker/bake-action@v6.9.0 uses: docker/bake-action@v6.5.0
with: with:
source: . source: .
targets: bench-test targets: bench-test
@@ -52,7 +52,7 @@ jobs:
- name: Push - name: Push
if: ${{ github.repository == 'frappe/frappe_docker' && github.event_name != 'pull_request' }} if: ${{ github.repository == 'frappe/frappe_docker' && github.event_name != 'pull_request' }}
uses: docker/bake-action@v6.9.0 uses: docker/bake-action@v6.5.0
with: with:
targets: bench targets: bench
push: true push: true

View File

@@ -27,7 +27,7 @@ jobs:
version: develop version: develop
push: ${{ github.repository == 'frappe/frappe_docker' && github.event_name != 'pull_request' }} push: ${{ github.repository == 'frappe/frappe_docker' && github.event_name != 'pull_request' }}
python_version: 3.11.6 python_version: 3.11.6
node_version: 20.19.2 node_version: 18.18.2
secrets: secrets:
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }} DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}

View File

@@ -49,7 +49,7 @@ jobs:
version: "15" version: "15"
push: ${{ github.repository == 'frappe/frappe_docker' && github.event_name != 'pull_request' }} push: ${{ github.repository == 'frappe/frappe_docker' && github.event_name != 'pull_request' }}
python_version: 3.11.6 python_version: 3.11.6
node_version: 20.19.2 node_version: 18.18.2
secrets: secrets:
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }} DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
@@ -62,10 +62,10 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v5 uses: actions/checkout@v4
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v6 uses: actions/setup-python@v5
with: with:
python-version: "3.10" python-version: "3.10"
@@ -100,7 +100,7 @@ jobs:
steps: steps:
- name: Setup deploy key - name: Setup deploy key
uses: webfactory/ssh-agent@v0.9.1 uses: webfactory/ssh-agent@v0.8.0
with: with:
ssh-private-key: ${{ secrets.HELM_DEPLOY_KEY }} ssh-private-key: ${{ secrets.HELM_DEPLOY_KEY }}

View File

@@ -34,7 +34,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
services: services:
registry: registry:
image: docker.io/registry:2 image: registry:2
ports: ports:
- 5000:5000 - 5000:5000
strategy: strategy:
@@ -43,7 +43,7 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v5 uses: actions/checkout@v4
- name: Setup QEMU - name: Setup QEMU
uses: docker/setup-qemu-action@v3 uses: docker/setup-qemu-action@v3
@@ -53,9 +53,6 @@ jobs:
- name: Setup Buildx - name: Setup Buildx
uses: docker/setup-buildx-action@v3 uses: docker/setup-buildx-action@v3
with:
driver-opts: network=host
platforms: linux/${{ matrix.arch }}
- name: Get latest versions - name: Get latest versions
run: python3 ./.github/scripts/get_latest_tags.py --repo ${{ inputs.repo }} --version ${{ inputs.version }} run: python3 ./.github/scripts/get_latest_tags.py --repo ${{ inputs.repo }} --version ${{ inputs.version }}
@@ -66,7 +63,7 @@ jobs:
echo "NODE_VERSION=${{ inputs.node_version }}" >> "$GITHUB_ENV" echo "NODE_VERSION=${{ inputs.node_version }}" >> "$GITHUB_ENV"
- name: Build - name: Build
uses: docker/bake-action@v6.9.0 uses: docker/bake-action@v6.5.0
with: with:
source: . source: .
push: true push: true
@@ -74,7 +71,7 @@ jobs:
REGISTRY_USER: localhost:5000/frappe REGISTRY_USER: localhost:5000/frappe
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v6 uses: actions/setup-python@v5
with: with:
python-version: "3.10" python-version: "3.10"
@@ -95,7 +92,7 @@ jobs:
- name: Push - name: Push
if: ${{ inputs.push }} if: ${{ inputs.push }}
uses: docker/bake-action@v6.9.0 uses: docker/bake-action@v6.5.0
with: with:
push: true push: true
set: "*.platform=linux/amd64,linux/arm64" set: "*.platform=linux/amd64,linux/arm64"

View File

@@ -13,16 +13,16 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v5 uses: actions/checkout@v4
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v6 uses: actions/setup-python@v5
with: with:
python-version: "3.10.6" python-version: "3.10.6"
# For shfmt pre-commit hook # For shfmt pre-commit hook
- name: Setup Go - name: Setup Go
uses: actions/setup-go@v6 uses: actions/setup-go@v5
with: with:
go-version: "^1.14" go-version: "^1.14"

View File

@@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v5 uses: actions/checkout@v4
- name: Update pre-commit hooks - name: Update pre-commit hooks
uses: vrslev/pre-commit-autoupdate@v1.0.0 uses: vrslev/pre-commit-autoupdate@v1.0.0

View File

@@ -9,7 +9,7 @@ jobs:
stale: stale:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/stale@v10 - uses: actions/stale@v9
with: with:
repo-token: ${{ secrets.GITHUB_TOKEN }} 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-issue-message: This issue has been automatically marked as stale. You have a week to explain why you believe this is an error.

View File

@@ -30,7 +30,7 @@ Then run: `docker compose -f pwd.yml up -d`
### To run on ARM64 architecture follow this instructions ### To run on ARM64 architecture follow this instructions
After you clone the repo and `cd frappe_docker`, run this command to build multi-architecture images specifically for ARM64. After cloning the repo run this command to build multi-architecture images specifically for ARM64.
`docker buildx bake --no-cache --set "*.platform=linux/arm64"` `docker buildx bake --no-cache --set "*.platform=linux/arm64"`
@@ -53,9 +53,9 @@ If you ran in a Dev Docker environment, to view container logs: `docker compose
### [Production](#production) ### [Production](#production)
- [List of containers](docs/container-setup/01-overview.md) - [List of containers](docs/list-of-containers.md)
- [Single Compose Setup](docs/single-compose-setup.md) - [Single Compose Setup](docs/single-compose-setup.md)
- [Environment Variables](docs/container-setup/env-variables.md) - [Environment Variables](docs/environment-variables.md)
- [Single Server Example](docs/single-server-example.md) - [Single Server Example](docs/single-server-example.md)
- [Setup Options](docs/setup-options.md) - [Setup Options](docs/setup-options.md)
- [Site Operations](docs/site-operations.md) - [Site Operations](docs/site-operations.md)
@@ -67,7 +67,8 @@ If you ran in a Dev Docker environment, to view container logs: `docker compose
### [Custom Images](#custom-images) ### [Custom Images](#custom-images)
- [Custom Apps](docs/container-setup/02-build-setup.md) - [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) - [Build Version 10 Images](docs/build-version-10-images.md)
### [Development](#development) ### [Development](#development)

View File

@@ -1,18 +0,0 @@
[
{
"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"
},
{
"url": "https://github.com/frappe/payments",
"branch": "version-15"
}
]

View File

@@ -1,237 +0,0 @@
name: frappe_docker
services:
backend:
depends_on:
configurator:
condition: service_completed_successfully
required: true
image: frappe/erpnext:v15.83.0
networks:
default: null
platform: linux/amd64
pull_policy: always
restart: unless-stopped
volumes:
- type: volume
source: sites
target: /home/frappe/frappe-bench/sites
volume: {}
configurator:
command:
- |
ls -1 apps > sites/apps.txt; bench set-config -g db_host $$DB_HOST; bench set-config -gp db_port $$DB_PORT; bench set-config -g redis_cache "redis://$$REDIS_CACHE"; 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;
depends_on:
db:
condition: service_healthy
required: true
redis-cache:
condition: service_started
required: true
redis-queue:
condition: service_started
required: true
entrypoint:
- bash
- -c
environment:
DB_HOST: db
DB_PORT: "3306"
REDIS_CACHE: redis-cache:6379
REDIS_QUEUE: redis-queue:6379
SOCKETIO_PORT: "9000"
image: frappe/erpnext:v15.83.0
networks:
default: null
platform: linux/amd64
pull_policy: always
restart: on-failure
volumes:
- type: volume
source: sites
target: /home/frappe/frappe-bench/sites
volume: {}
db:
command:
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_unicode_ci
- --skip-character-set-client-handshake
- --skip-innodb-read-only-compressed
environment:
MARIADB_AUTO_UPGRADE: "1"
MYSQL_ROOT_PASSWORD: khaelicloud
healthcheck:
test:
- CMD
- healthcheck.sh
- --connect
- --innodb_initialized
timeout: 5s
interval: 5s
retries: 5
start_period: 5s
image: mariadb:11.8
networks:
default: null
restart: unless-stopped
volumes:
- type: volume
source: db-data
target: /var/lib/mysql
volume: {}
frontend:
command:
- nginx-entrypoint.sh
depends_on:
backend:
condition: service_started
required: true
websocket:
condition: service_started
required: true
environment:
BACKEND: backend:8000
CLIENT_MAX_BODY_SIZE: 50m
FRAPPE_SITE_NAME_HEADER: $$host
PROXY_READ_TIMEOUT: "120"
SOCKETIO: websocket:9000
UPSTREAM_REAL_IP_ADDRESS: 127.0.0.1
UPSTREAM_REAL_IP_HEADER: X-Forwarded-For
UPSTREAM_REAL_IP_RECURSIVE: "off"
image: frappe/erpnext:v15.83.0
networks:
default: null
platform: linux/amd64
ports:
- mode: ingress
target: 8080
published: "8080"
protocol: tcp
pull_policy: always
restart: unless-stopped
volumes:
- type: volume
source: sites
target: /home/frappe/frappe-bench/sites
volume: {}
queue-long:
command:
- bench
- worker
- --queue
- long,default,short
depends_on:
configurator:
condition: service_completed_successfully
required: true
image: frappe/erpnext:v15.83.0
networks:
default: null
platform: linux/amd64
pull_policy: always
restart: unless-stopped
volumes:
- type: volume
source: sites
target: /home/frappe/frappe-bench/sites
volume: {}
queue-short:
command:
- bench
- worker
- --queue
- short,default
depends_on:
configurator:
condition: service_completed_successfully
required: true
image: frappe/erpnext:v15.83.0
networks:
default: null
platform: linux/amd64
pull_policy: always
restart: unless-stopped
volumes:
- type: volume
source: sites
target: /home/frappe/frappe-bench/sites
volume: {}
redis-cache:
image: redis:6.2-alpine
networks:
default: null
restart: unless-stopped
redis-queue:
image: redis:6.2-alpine
networks:
default: null
restart: unless-stopped
volumes:
- type: volume
source: redis-queue-data
target: /data
volume: {}
scheduler:
command:
- bench
- schedule
depends_on:
configurator:
condition: service_completed_successfully
required: true
image: frappe/erpnext:v15.83.0
networks:
default: null
platform: linux/amd64
pull_policy: always
restart: unless-stopped
volumes:
- type: volume
source: sites
target: /home/frappe/frappe-bench/sites
volume: {}
websocket:
command:
- node
- /home/frappe/frappe-bench/apps/frappe/socketio.js
depends_on:
configurator:
condition: service_completed_successfully
required: true
image: frappe/erpnext:v15.83.0
networks:
default: null
platform: linux/amd64
pull_policy: always
restart: unless-stopped
volumes:
- type: volume
source: sites
target: /home/frappe/frappe-bench/sites
volume: {}
networks:
default:
name: frappe_docker_default
volumes:
db-data:
name: frappe_docker_db-data
redis-queue-data:
name: frappe_docker_redis-queue-data
sites:
name: frappe_docker_sites
x-backend-defaults:
depends_on:
configurator:
condition: service_completed_successfully
image: frappe/erpnext:v15.83.0
pull_policy: always
restart: unless-stopped
volumes:
- sites:/home/frappe/frappe-bench/sites
x-customizable-image:
image: frappe/erpnext:v15.83.0
pull_policy: always
restart: unless-stopped
x-depends-on-configurator:
depends_on:
configurator:
condition: service_completed_successfully

View File

@@ -1,7 +1,8 @@
version: "3.7" version: "3.7"
services: services:
mariadb: mariadb:
image: docker.io/mariadb:11.8 image: docker.io/mariadb:10.6
platform: linux/amd64
command: command:
- --character-set-server=utf8mb4 - --character-set-server=utf8mb4
- --collation-server=utf8mb4_unicode_ci - --collation-server=utf8mb4_unicode_ci
@@ -9,13 +10,12 @@ services:
- --skip-innodb-read-only-compressed # Temporary fix for MariaDB 10.6 - --skip-innodb-read-only-compressed # Temporary fix for MariaDB 10.6
environment: environment:
MYSQL_ROOT_PASSWORD: 123 MYSQL_ROOT_PASSWORD: 123
MARIADB_AUTO_UPGRADE: 1
volumes: volumes:
- mariadb-data:/var/lib/mysql - mariadb-data:/var/lib/mysql
# Enable PostgreSQL only if you use it, see development/README.md for more information. # Enable PostgreSQL only if you use it, see development/README.md for more information.
# postgresql: # postgresql:
# image: postgres:14 # image: postgres:11.8
# environment: # environment:
# POSTGRES_PASSWORD: 123 # POSTGRES_PASSWORD: 123
# volumes: # volumes:
@@ -38,14 +38,15 @@ services:
redis-cache: redis-cache:
image: docker.io/redis:alpine image: docker.io/redis:alpine
platform: linux/amd64
redis-queue: redis-queue:
image: docker.io/redis:alpine image: docker.io/redis:alpine
platform: linux/amd64
frappe: frappe:
image: docker.io/frappe/bench:latest image: docker.io/frappe/bench:latest
# If you want to build the current bench image the Containerfile is in this Repo. platform: linux/amd64
# build: ../images/bench
command: sleep infinity command: sleep infinity
environment: environment:
- SHELL=/bin/bash - SHELL=/bin/bash

View File

@@ -6,7 +6,7 @@
"configurations": [ "configurations": [
{ {
"name": "Bench Web", "name": "Bench Web",
"type": "debugpy", "type": "python",
"request": "launch", "request": "launch",
"program": "${workspaceFolder}/frappe-bench/apps/frappe/frappe/utils/bench_helper.py", "program": "${workspaceFolder}/frappe-bench/apps/frappe/frappe/utils/bench_helper.py",
"args": [ "args": [
@@ -17,6 +17,7 @@
"--noreload", "--noreload",
"--nothreading" "--nothreading"
], ],
"pythonPath": "${workspaceFolder}/frappe-bench/env/bin/python",
"cwd": "${workspaceFolder}/frappe-bench/sites", "cwd": "${workspaceFolder}/frappe-bench/sites",
"env": { "env": {
"DEV_SERVER": "1" "DEV_SERVER": "1"
@@ -24,21 +25,11 @@
}, },
{ {
"name": "Bench Short Worker", "name": "Bench Short Worker",
"type": "debugpy", "type": "python",
"request": "launch", "request": "launch",
"program": "${workspaceFolder}/frappe-bench/apps/frappe/frappe/utils/bench_helper.py", "program": "${workspaceFolder}/frappe-bench/apps/frappe/frappe/utils/bench_helper.py",
"args": ["frappe", "worker", "--queue", "short"], "args": ["frappe", "worker", "--queue", "short"],
"cwd": "${workspaceFolder}/frappe-bench/sites", "pythonPath": "${workspaceFolder}/frappe-bench/env/bin/python",
"env": {
"DEV_SERVER": "1"
}
},
{
"name": "Bench Default Worker",
"type": "debugpy",
"request": "launch",
"program": "${workspaceFolder}/frappe-bench/apps/frappe/frappe/utils/bench_helper.py",
"args": ["frappe", "worker", "--queue", "default"],
"cwd": "${workspaceFolder}/frappe-bench/sites", "cwd": "${workspaceFolder}/frappe-bench/sites",
"env": { "env": {
"DEV_SERVER": "1" "DEV_SERVER": "1"
@@ -46,10 +37,11 @@
}, },
{ {
"name": "Bench Long Worker", "name": "Bench Long Worker",
"type": "debugpy", "type": "python",
"request": "launch", "request": "launch",
"program": "${workspaceFolder}/frappe-bench/apps/frappe/frappe/utils/bench_helper.py", "program": "${workspaceFolder}/frappe-bench/apps/frappe/frappe/utils/bench_helper.py",
"args": ["frappe", "worker", "--queue", "long"], "args": ["frappe", "worker", "--queue", "long"],
"pythonPath": "${workspaceFolder}/frappe-bench/env/bin/python",
"cwd": "${workspaceFolder}/frappe-bench/sites", "cwd": "${workspaceFolder}/frappe-bench/sites",
"env": { "env": {
"DEV_SERVER": "1" "DEV_SERVER": "1"
@@ -57,21 +49,21 @@
}, },
{ {
"name": "Honcho SocketIO Watch Schedule Worker", "name": "Honcho SocketIO Watch Schedule Worker",
"type": "debugpy", "type": "python",
"request": "launch", "request": "launch",
"python": "/home/frappe/.pyenv/shims/python",
"program": "/home/frappe/.local/bin/honcho", "program": "/home/frappe/.local/bin/honcho",
"pythonPath": "${workspaceFolder}/frappe-bench/env/bin/python",
"cwd": "${workspaceFolder}/frappe-bench", "cwd": "${workspaceFolder}/frappe-bench",
"console": "internalConsole", "console": "internalConsole",
"args": ["start", "socketio", "watch", "schedule", "worker"], "args": [
"postDebugTask": "Clean Honcho SocketIO Watch Schedule Worker" "start",
} "socketio",
], "watch",
"compounds": [ "schedule",
{ "worker_short",
"name": "Honcho + Web debug", "worker_long",
"configurations": ["Bench Web", "Honcho SocketIO Watch Schedule Worker"], "worker_default"
"stopAll": true ]
} }
] ]
} }

View File

@@ -1,22 +0,0 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"label": "Clean Honcho SocketIO Watch Schedule Worker",
"detail": "When stopping the debug process from vscode window, the honcho won't receive the SIGINT signal. This task will send the SIGINT signal to the honcho processes.",
"type": "shell",
"command": "pkill -SIGINT -f bench; pkill -SIGINT -f socketio",
"isBackground": false,
"presentation": {
"echo": true,
"reveal": "silent",
"focus": false,
"panel": "shared",
"showReuseMessage": false,
"close": true
}
}
]
}

View File

@@ -9,7 +9,7 @@ variable PYTHON_VERSION {
default = "3.11.6" default = "3.11.6"
} }
variable NODE_VERSION { variable NODE_VERSION {
default = "20.19.2" default = "18.18.2"
} }
variable "FRAPPE_VERSION" { variable "FRAPPE_VERSION" {

View File

@@ -1,47 +0,0 @@
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 youre 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 [overrider.md](overrider.md) for the complete list of available overrides and how to use them.
---
**Next:** [Build Setup →](02-build-setup.md)

View File

@@ -1,121 +0,0 @@
This guide walks you through building Frappe images from the repository resources.
# Prerequisites
- git
- docker or podman
- docker compose v2 or podman compose
> Install containerization software according to the official maintainer documentation. Avoid package managers when not recommended, as they frequently cause compatibility issues.
# Clone this repo
```bash
git clone https://github.com/frappe/frappe_docker
cd frappe_docker
```
# Define custom apps
If you dont want to install specific apps to the image skip this section.
To include custom apps in your image, create an `apps.json` file in the repository root:
```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"
}
]
```
Then generate a base64-encoded string from this file:
```bash
export APPS_JSON_BASE64=$(base64 -w 0 apps.json)
```
# Build the image
Choose the appropriate build command based on your container runtime and desired image type. This example builds the `layered` image with the custom `apps.json` you created.
`Docker`:
```bash
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=custom:15 \
--file=images/layered/Containerfile .
```
`Podman`:
```bash
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 .
```
## Build args
| Arg | Purpose |
| -------------------- | --------------------------------------------------------------------------------------------- |
| **Frappe Framework** | |
| FRAPPE_PATH | Repository URL for Frappe framework source code. Defaults to https://github.com/frappe/frappe |
| FRAPPE_BRANCH | Branch to use for Frappe framework. Defaults to version-15 |
| **Custom Apps** | |
| APPS_JSON_BASE64 | Base64-encoded JSON string from apps.json defining apps to install |
| **Dependencies** | |
| PYTHON_VERSION | Python version for the base image |
| NODE_VERSION | Node.js version |
| WKHTMLTOPDF_VERSION | wkhtmltopdf version |
| **bench only** | |
| DEBIAN_BASE | Debian base version for the bench image, defaults to `bookworm` |
| WKHTMLTOPDF_DISTRO | use the specified distro for debian package. Default is `bookworm` |
# 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](env-variables.md) for detailed descriptions of all available variables.
# 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 example.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)

View File

@@ -1,42 +0,0 @@
# 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.
```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.
> ## 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)

View File

@@ -1,112 +0,0 @@
# 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 |
---
## HTTPS & SSL Configuration
| Variable | Purpose | Default | When to Set |
| ------------------- | ------------------------------------------------ | ------- | ---------------------------------------- |
| `LETSENCRYPT_EMAIL` | Email for Let's Encrypt certificate registration | — | Required if using HTTPS override |
| `SITES` | List of domains for SSL certificates | — | Required if using reverse proxy override |
**Format for `SITES`:**
```bash
# Single site
SITES=`mysite.example.com`
# Wildcard (any subdomain)
SITES=`{any:.+}`
```
---
## 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` |
---
## Nginx Proxy Configuration
| Variable | Purpose | Default | Allowed Values |
| ---------------------- | ---------------------------------- | -------------- | -------------------------------------------- |
| `BACKEND` | Backend service address and port | `0.0.0.0:8000` | `{host}:{port}` |
| `SOCKETIO` | Socket.IO service address and port | `0.0.0.0:9000` | `{host}:{port}` |
| `HTTP_PUBLISH_PORT` | Published HTTP port | `8080` | Any available port |
| `PROXY_READ_TIMEOUT` | Upstream request timeout | `120s` | Any nginx timeout value (e.g., `300s`, `5m`) |
| `CLIENT_MAX_BODY_SIZE` | Maximum upload file size | `50m` | Any nginx size value (e.g., `100m`, `1g`) |
### 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` |

View File

@@ -1,27 +0,0 @@
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` and `LETSENCRYPT_EMAIL` must be set. `HTTP_PUBLISH_PORT` and `HTTPS_PUBLISH_PORT` can be set. |
| **Redis** | | |
| compose.redis.yaml | Adds Redis service for caching and background job queuing |
| **TBD** | **The following overrides are available but lack documentation. If you use them and understand their purpose, please consider contributing to this documentation.** |
| compose.backup-cron.yaml | | |
| compose.custom-domain-ssl.yaml | | |
| compose.custom-domain.yaml | | |
| compose.multi-bench-ssl.yaml | | |
| compose.multi-bench.yaml | | |
| compose.traefik-ssl.yaml | | |
| compose.traefik.yaml | | |

125
docs/custom-apps-podman.md Normal file
View File

@@ -0,0 +1,125 @@
## 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'@'%'`

156
docs/custom-apps.md Normal file
View File

@@ -0,0 +1,156 @@
### 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=18.20.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 `18.18.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
```

View File

@@ -221,11 +221,6 @@ bench --site development.localhost install-app erpnext
``` ```
Note: Both frappe and erpnext must be on branch with same name. e.g. version-14 Note: Both frappe and erpnext must be on branch with same name. e.g. version-14
You can use the `switch-to-branch` command to align versions if you get an error about mismatching versions.
```shell
bench switch-to-branch version-xx
```
### Start Frappe without debugging ### Start Frappe without debugging

View File

@@ -0,0 +1,56 @@
## 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_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.

View File

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

View File

@@ -65,7 +65,7 @@ Create a file called `traefik.env` in `~/gitops`
```shell ```shell
echo 'TRAEFIK_DOMAIN=traefik.example.com' > ~/gitops/traefik.env echo 'TRAEFIK_DOMAIN=traefik.example.com' > ~/gitops/traefik.env
echo 'EMAIL=admin@example.com' >> ~/gitops/traefik.env echo 'EMAIL=admin@example.com' >> ~/gitops/traefik.env
echo "HASHED_PASSWORD='$(openssl passwd -apr1 changeit)'" >> ~/gitops/traefik.env echo 'HASHED_PASSWORD='$(openssl passwd -apr1 changeit | sed -e s/\\$/\\$\\$/g) >> ~/gitops/traefik.env
``` ```
Note: Note:

View File

@@ -7,7 +7,6 @@
Note: 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. - 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.
- Also you have to pass `-p <project_name>` if `-p` passed previously eg. `docker-compose -p <project_name> exec (rest of the command)`.
```sh ```sh
docker-compose exec backend bench new-site --mariadb-user-host-login-scope=% --db-root-password <db-password> --admin-password <admin-password> <site-name> docker-compose exec backend bench new-site --mariadb-user-host-login-scope=% --db-root-password <db-password> --admin-password <admin-password> <site-name>

View File

@@ -1 +0,0 @@
WwogICAgewogICAgICAgICJ1cmwiOiAiaHR0cHM6Ly9naXRodWIuY29tL2ZyYXBwZS9lcnBuZXh0IiwKICAgICAgICAiYnJhbmNoIjogInZlcnNpb24tMTUiCiAgICB9LAogICAgewogICAgICAgICJ1cmwiOiAiaHR0cHM6Ly9naXRodWIuY29tL2ZyYXBwZS9ocm1zIiwKICAgICAgICAiYnJhbmNoIjogInZlcnNpb24tMTUiCiAgICB9LAogICAgewogICAgICAgICJ1cmwiOiAiaHR0cHM6Ly9naXRodWIuY29tL2ZyYXBwZS9oZWxwZGVzayIsCiAgICAgICAgImJyYW5jaCI6ICJtYWluIgogICAgfSwKICAgIHsKICAgICAgICAidXJsIjogImh0dHBzOi8vZ2l0aHViLmNvbS9mcmFwcGUvcGF5bWVudHMiLAogICAgICAgICJicmFuY2giOiAidmVyc2lvbi0xNSIKICAgIH0KXQ==

View File

@@ -1,12 +1,9 @@
# Reference: https://github.com/frappe/frappe_docker/blob/main/docs/environment-variables.md # Reference: https://github.com/frappe/frappe_docker/blob/main/docs/environment-variables.md
ERPNEXT_VERSION=v15.83.0 ERPNEXT_VERSION=v15.54.4
DB_PASSWORD=123 DB_PASSWORD=123
#Only if you use docker secrets for the db password
DB_PASSWORD_SECRETS_FILE=
# Only if you use external database # Only if you use external database
DB_HOST= DB_HOST=
DB_PORT= DB_PORT=
@@ -16,7 +13,7 @@ REDIS_CACHE=
REDIS_QUEUE= REDIS_QUEUE=
# Only with HTTPS override # Only with HTTPS override
LETSENCRYPT_EMAIL=admin@khaeli.com LETSENCRYPT_EMAIL=mail@example.com
# These environment variables are not required. # These environment variables are not required.
@@ -52,4 +49,4 @@ CLIENT_MAX_BODY_SIZE=
# List of sites for letsencrypt certificates quoted with backtick (`) and separated by comma (,) # List of sites for letsencrypt certificates quoted with backtick (`) and separated by comma (,)
# More https://doc.traefik.io/traefik/routing/routers/#rule # More https://doc.traefik.io/traefik/routing/routers/#rule
# About acme https://doc.traefik.io/traefik/https/acme/#domain-definition # About acme https://doc.traefik.io/traefik/https/acme/#domain-definition
SITES=`erp.khaeli.com`,`hr.khaeli.com`,`helpdesk.khaeli.com`,`payments.khaeli.com` SITES=`erp.example.com`

View File

@@ -44,10 +44,10 @@ RUN apt-get update \
liblcms2-dev \ liblcms2-dev \
libldap2-dev \ libldap2-dev \
libmariadb-dev \ libmariadb-dev \
pkg-config \
libsasl2-dev \ libsasl2-dev \
libtiff5-dev \ libtiff5-dev \
libwebp-dev \ libwebp-dev \
pkg-config \
redis-tools \ redis-tools \
rlwrap \ rlwrap \
tk8.6-dev \ tk8.6-dev \
@@ -125,7 +125,7 @@ RUN git clone ${GIT_REPO} --depth 1 -b ${GIT_BRANCH} .bench \
# Install Node via nvm # Install Node via nvm
ENV NODE_VERSION_14=16.20.2 ENV NODE_VERSION_14=16.20.2
ENV NODE_VERSION=20.19.2 ENV NODE_VERSION=18.18.2
ENV NVM_DIR=/home/frappe/.nvm ENV NVM_DIR=/home/frappe/.nvm
ENV PATH=${NVM_DIR}/versions/node/v${NODE_VERSION}/bin/:${PATH} ENV PATH=${NVM_DIR}/versions/node/v${NODE_VERSION}/bin/:${PATH}

View File

@@ -7,7 +7,7 @@ COPY resources/nginx-entrypoint.sh /usr/local/bin/nginx-entrypoint.sh
ARG WKHTMLTOPDF_VERSION=0.12.6.1-3 ARG WKHTMLTOPDF_VERSION=0.12.6.1-3
ARG WKHTMLTOPDF_DISTRO=bookworm ARG WKHTMLTOPDF_DISTRO=bookworm
ARG NODE_VERSION=20.19.2 ARG NODE_VERSION=18.18.2
ENV NVM_DIR=/home/frappe/.nvm ENV NVM_DIR=/home/frappe/.nvm
ENV PATH=${NVM_DIR}/versions/node/v${NODE_VERSION}/bin/:${PATH} ENV PATH=${NVM_DIR}/versions/node/v${NODE_VERSION}/bin/:${PATH}
@@ -94,7 +94,6 @@ RUN apt-get update \
libsasl2-dev \ libsasl2-dev \
libtiff5-dev \ libtiff5-dev \
libwebp-dev \ libwebp-dev \
pkg-config \
redis-tools \ redis-tools \
rlwrap \ rlwrap \
tk8.6-dev \ tk8.6-dev \

View File

@@ -4,7 +4,7 @@ FROM python:${PYTHON_VERSION}-slim-${DEBIAN_BASE} AS base
ARG WKHTMLTOPDF_VERSION=0.12.6.1-3 ARG WKHTMLTOPDF_VERSION=0.12.6.1-3
ARG WKHTMLTOPDF_DISTRO=bookworm ARG WKHTMLTOPDF_DISTRO=bookworm
ARG NODE_VERSION=20.19.2 ARG NODE_VERSION=18.18.2
ENV NVM_DIR=/home/frappe/.nvm ENV NVM_DIR=/home/frappe/.nvm
ENV PATH=${NVM_DIR}/versions/node/v${NODE_VERSION}/bin/:${PATH} ENV PATH=${NVM_DIR}/versions/node/v${NODE_VERSION}/bin/:${PATH}
@@ -86,7 +86,6 @@ RUN apt-get update \
libsasl2-dev \ libsasl2-dev \
libtiff5-dev \ libtiff5-dev \
libwebp-dev \ libwebp-dev \
pkg-config \
redis-tools \ redis-tools \
rlwrap \ rlwrap \
tk8.6-dev \ tk8.6-dev \
@@ -122,7 +121,7 @@ FROM base AS erpnext
USER frappe USER frappe
RUN echo "echo \"Commands restricted in prodution container, Read FAQ before you proceed: https://frappe.io/ctr-faq\"" >> ~/.bashrc RUN echo "echo \"Commands restricted in prodution container, Read FAQ before you proceed: https://frappe.fyi/ctr-faq\"" >> ~/.bashrc
COPY --from=builder --chown=frappe:frappe /home/frappe/frappe-bench /home/frappe/frappe-bench COPY --from=builder --chown=frappe:frappe /home/frappe/frappe-bench /home/frappe/frappe-bench

View File

@@ -1,11 +0,0 @@
services:
db:
environment:
MYSQL_ROOT_PASSWORD: !reset null
MYSQL_ROOT_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
secrets:
db_password:
file: ${DB_PASSWORD_SECRETS_FILE:?No db secret file set}

View File

@@ -3,14 +3,12 @@ version: "3.3"
services: services:
database: database:
container_name: mariadb-database container_name: mariadb-database
image: mariadb:11.8 image: mariadb:10.6
restart: unless-stopped restart: unless-stopped
healthcheck: healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"] test: mysqladmin ping -h localhost --password=${DB_PASSWORD:-changeit}
start_period: 5s interval: 1s
interval: 5s retries: 20
timeout: 5s
retries: 5
command: command:
- --character-set-server=utf8mb4 - --character-set-server=utf8mb4
- --collation-server=utf8mb4_unicode_ci - --collation-server=utf8mb4_unicode_ci
@@ -18,7 +16,6 @@ services:
- --skip-innodb-read-only-compressed - --skip-innodb-read-only-compressed
environment: environment:
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD:-changeit} MYSQL_ROOT_PASSWORD: ${DB_PASSWORD:-changeit}
MARIADB_AUTO_UPGRADE: 1
volumes: volumes:
- db-data:/var/lib/mysql - db-data:/var/lib/mysql
networks: networks:

View File

@@ -8,13 +8,11 @@ services:
condition: service_healthy condition: service_healthy
db: db:
image: mariadb:11.8 image: mariadb:10.6
healthcheck: healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"] test: mysqladmin ping -h localhost --password=${DB_PASSWORD}
start_period: 5s interval: 1s
interval: 5s retries: 20
timeout: 5s
retries: 5
restart: unless-stopped restart: unless-stopped
command: command:
- --character-set-server=utf8mb4 - --character-set-server=utf8mb4
@@ -22,8 +20,7 @@ services:
- --skip-character-set-client-handshake - --skip-character-set-client-handshake
- --skip-innodb-read-only-compressed # Temporary fix for MariaDB 10.6 - --skip-innodb-read-only-compressed # Temporary fix for MariaDB 10.6
environment: environment:
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD:-123} MYSQL_ROOT_PASSWORD: ${DB_PASSWORD:?No db password set}
MARIADB_AUTO_UPGRADE: 1
volumes: volumes:
- db-data:/var/lib/mysql - db-data:/var/lib/mysql

25
pwd.yml
View File

@@ -2,7 +2,7 @@ version: "3"
services: services:
backend: backend:
image: frappe/erpnext:v15.83.0 image: frappe/erpnext:v15.54.4
networks: networks:
- frappe_network - frappe_network
deploy: deploy:
@@ -18,7 +18,7 @@ services:
MARIADB_ROOT_PASSWORD: admin MARIADB_ROOT_PASSWORD: admin
configurator: configurator:
image: frappe/erpnext:v15.83.0 image: frappe/erpnext:v15.54.4
networks: networks:
- frappe_network - frappe_network
deploy: deploy:
@@ -47,7 +47,7 @@ services:
- logs:/home/frappe/frappe-bench/logs - logs:/home/frappe/frappe-bench/logs
create-site: create-site:
image: frappe/erpnext:v15.83.0 image: frappe/erpnext:v15.54.4
networks: networks:
- frappe_network - frappe_network
deploy: deploy:
@@ -102,7 +102,7 @@ services:
- db-data:/var/lib/mysql - db-data:/var/lib/mysql
frontend: frontend:
image: frappe/erpnext:v15.83.0 image: frappe/erpnext:v15.54.4
networks: networks:
- frappe_network - frappe_network
depends_on: depends_on:
@@ -128,7 +128,7 @@ services:
- "8080:8080" - "8080:8080"
queue-long: queue-long:
image: frappe/erpnext:v15.83.0 image: frappe/erpnext:v15.54.4
networks: networks:
- frappe_network - frappe_network
deploy: deploy:
@@ -142,12 +142,9 @@ services:
volumes: volumes:
- sites:/home/frappe/frappe-bench/sites - sites:/home/frappe/frappe-bench/sites
- logs:/home/frappe/frappe-bench/logs - logs:/home/frappe/frappe-bench/logs
environment:
FRAPPE_REDIS_CACHE: redis://redis-cache:6379
FRAPPE_REDIS_QUEUE: redis://redis-queue:6379
queue-short: queue-short:
image: frappe/erpnext:v15.83.0 image: frappe/erpnext:v15.54.4
networks: networks:
- frappe_network - frappe_network
deploy: deploy:
@@ -161,9 +158,6 @@ services:
volumes: volumes:
- sites:/home/frappe/frappe-bench/sites - sites:/home/frappe/frappe-bench/sites
- logs:/home/frappe/frappe-bench/logs - logs:/home/frappe/frappe-bench/logs
environment:
FRAPPE_REDIS_CACHE: redis://redis-cache:6379
FRAPPE_REDIS_QUEUE: redis://redis-queue:6379
redis-queue: redis-queue:
image: redis:6.2-alpine image: redis:6.2-alpine
@@ -184,7 +178,7 @@ services:
condition: on-failure condition: on-failure
scheduler: scheduler:
image: frappe/erpnext:v15.83.0 image: frappe/erpnext:v15.54.4
networks: networks:
- frappe_network - frappe_network
deploy: deploy:
@@ -198,7 +192,7 @@ services:
- logs:/home/frappe/frappe-bench/logs - logs:/home/frappe/frappe-bench/logs
websocket: websocket:
image: frappe/erpnext:v15.83.0 image: frappe/erpnext:v15.54.4
networks: networks:
- frappe_network - frappe_network
deploy: deploy:
@@ -207,9 +201,6 @@ services:
command: command:
- node - node
- /home/frappe/frappe-bench/apps/frappe/socketio.js - /home/frappe/frappe-bench/apps/frappe/socketio.js
environment:
FRAPPE_REDIS_CACHE: redis://redis-cache:6379
FRAPPE_REDIS_QUEUE: redis://redis-queue:6379
volumes: volumes:
- sites:/home/frappe/frappe-bench/sites - sites:/home/frappe/frappe-bench/sites
- logs:/home/frappe/frappe-bench/logs - logs:/home/frappe/frappe-bench/logs

View File

@@ -1 +1 @@
pytest==8.4.2 pytest==8.3.5

View File

@@ -151,7 +151,6 @@ def s3_service(python_path: str, compose: Compose):
subprocess.check_call(cmd) subprocess.check_call(cmd)
compose("cp", "tests/_create_bucket.py", "backend:/tmp") compose("cp", "tests/_create_bucket.py", "backend:/tmp")
compose.exec("backend", "bench", "pip", "install", "boto3~=1.34.143")
compose.exec( compose.exec(
"-e", "-e",
f"S3_ACCESS_KEY={access_key}", f"S3_ACCESS_KEY={access_key}",