diff --git a/docs/02-setup/02-build-setup.md b/docs/02-setup/02-build-setup.md index 93331280..d411cab7 100644 --- a/docs/02-setup/02-build-setup.md +++ b/docs/02-setup/02-build-setup.md @@ -7,11 +7,13 @@ This guide walks you through building Frappe images from the repository resource # Prerequisites - git -- docker or podman +- docker (Engine **v23.0+**) 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 @@ -42,23 +44,19 @@ To include custom apps in your image, create an `apps.json` file in the reposito ] ``` -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. +> **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 \ --build-arg=FRAPPE_PATH=https://github.com/frappe/frappe \ --build-arg=FRAPPE_BRANCH=version-15 \ - --build-arg=APPS_JSON_BASE64=$APPS_JSON_BASE64 \ + --secret=id=apps_json,src=apps.json \ --tag=custom:15 \ --file=images/layered/Containerfile . ``` @@ -69,7 +67,7 @@ docker build \ 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 \ + --secret=id=apps_json,src=apps.json \ --tag=custom:15 \ --file=images/layered/Containerfile . ``` @@ -82,7 +80,7 @@ podman build \ | 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 | +| (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 | diff --git a/docs/02-setup/08-single-server-nginxproxy-example.md b/docs/02-setup/08-single-server-nginxproxy-example.md index 0049e39f..4f1c212f 100644 --- a/docs/02-setup/08-single-server-nginxproxy-example.md +++ b/docs/02-setup/08-single-server-nginxproxy-example.md @@ -15,7 +15,7 @@ We will setup the following: ## Requirements -- A server that can run Docker (recommended: 2 vCPU, 4 GB RAM, 50 GB SSD). +- 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` @@ -84,25 +84,17 @@ cat > ~/gitops/apps.json <<'EOF' EOF ``` -Generate the BASE64 value and build: +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 -export APPS_JSON_BASE64=$(base64 -w 0 ~/gitops/apps.json) - docker build \ --build-arg=FRAPPE_PATH=https://github.com/frappe/frappe \ --build-arg=FRAPPE_BRANCH=version-16 \ - --build-arg=APPS_JSON_BASE64=$APPS_JSON_BASE64 \ + --secret=id=apps_json,src=$HOME/gitops/apps.json \ --tag=my-erpnext-prod-image:16.0.0 \ --file=images/layered/Containerfile . ``` -If `base64 -w 0` is not available on your system, use: - -```shell -export APPS_JSON_BASE64=$(base64 ~/gitops/apps.json | tr -d '\n') -``` - ### Configure environment Create an environment file for the bench: diff --git a/images/custom/Containerfile b/images/custom/Containerfile index a8298b52..c7519e84 100644 --- a/images/custom/Containerfile +++ b/images/custom/Containerfile @@ -113,18 +113,13 @@ RUN apt-get update \ libbz2-dev \ && rm -rf /var/lib/apt/lists/* -# apps.json includes -ARG APPS_JSON_BASE64 -RUN if [ -n "${APPS_JSON_BASE64}" ]; then \ - mkdir /opt/frappe && echo "${APPS_JSON_BASE64}" | base64 -d > /opt/frappe/apps.json; \ - fi - USER frappe ARG FRAPPE_BRANCH=version-16 ARG FRAPPE_PATH=https://github.com/frappe/frappe -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 \ + 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}\ diff --git a/images/layered/Containerfile b/images/layered/Containerfile index 142c487a..de5e835d 100644 --- a/images/layered/Containerfile +++ b/images/layered/Containerfile @@ -4,18 +4,12 @@ FROM frappe/build:${FRAPPE_BRANCH} AS builder 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 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 \ + 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}\