From 99d9a1dc385d8a0660a67b0cbbfeac3d5229e58c Mon Sep 17 00:00:00 2001 From: RocketQuack <202538874+Rocket-Quack@users.noreply.github.com> Date: Fri, 6 Feb 2026 05:26:28 +0100 Subject: [PATCH] feat: add nginx-proxy with acme companion as an alternative to traefik (#1800) * refactor: move core nginx files into more recognizable folder structure * chore: include HTTPS_PUBLISH_PORT in example .env * feat: add nginx-proxy and acme-companion compose overrides * docs: add NGINX_PROXY_HOSTS to example.env * docs: add nginx-proxy overrides * docs: split docs, variables for usage of traefik or nginx-proxy * docs: update override notes for traefik proxy on separate stack * docs: split TLS/SSL overview and add own caddy guide * docs: add nginx-proxy + acme companion guide * docs: add nginx-proxy and acme single-server setup guide --- docs/02-setup/04-env-variables.md | 37 +++- docs/02-setup/05-overrides.md | 40 ++-- .../08-single-server-nginxproxy-example.md | 177 ++++++++++++++++++ docs/03-production/01-tls-ssl-setup.md | 60 ++++-- .../04-nginx-proxy-acme-companion.md | 82 ++++++++ docs/03-production/05-caddy-https.md | 44 +++++ .../02-windows-nginx-entrypoint-error.md | 2 +- docs/getting-started.md | 15 +- example.env | 9 + images/custom/Containerfile | 4 +- images/production/Containerfile | 4 +- overrides/compose.nginxproxy-ssl.yaml | 26 +++ overrides/compose.nginxproxy.yaml | 21 +++ .../{ => core/nginx}/nginx-entrypoint.sh | 0 .../{ => core/nginx}/nginx-template.conf | 0 15 files changed, 470 insertions(+), 51 deletions(-) create mode 100644 docs/02-setup/08-single-server-nginxproxy-example.md create mode 100644 docs/03-production/04-nginx-proxy-acme-companion.md create mode 100644 docs/03-production/05-caddy-https.md create mode 100644 overrides/compose.nginxproxy-ssl.yaml create mode 100644 overrides/compose.nginxproxy.yaml rename resources/{ => core/nginx}/nginx-entrypoint.sh (100%) rename resources/{ => core/nginx}/nginx-template.conf (100%) diff --git a/docs/02-setup/04-env-variables.md b/docs/02-setup/04-env-variables.md index a9c73a8a..54ed2956 100644 --- a/docs/02-setup/04-env-variables.md +++ b/docs/02-setup/04-env-variables.md @@ -43,12 +43,14 @@ Then edit `.env` and set variables according to your needs. --- -## HTTPS & SSL Configuration +## Reverse Proxy and SSL (HTTPS) Configuration -| Variable | Purpose | Default | When to Set | -| ------------------- | ------------------------------------------------------------- | ------- | ---------------------------------------- | -| `LETSENCRYPT_EMAIL` | Email for Let's Encrypt certificate registration | — | Required if using HTTPS override | -| `SITES_RULE` | List of domains for SSL (Traefik rule for TLS domain routing) | — | Required if using reverse proxy override | +### Traefik (compose.proxy.yaml / compose.https.yaml) + +| Variable | Purpose | Default | When to Set | +| ------------------- | ------------------------------------------------ | ------- | -------------------------------------------- | +| `LETSENCRYPT_EMAIL` | Email for Let's Encrypt certificate registration | - | Required for `compose.https.yaml` | +| `SITES_RULE` | Domains for routing (Traefik rule expression) | - | Required for Traefik routing/HTTPS overrides | **Format for `SITES_RULE`:** @@ -63,6 +65,28 @@ SITES_RULE=Host(`a.example.com`) || Host(`b.example.com`) > Note: The Traefik v3 migration is complete. Use `SITES_RULE` as a full v3 rule expression; `SITES` is deprecated. > Rule syntax now defaults to v3, so no `core.defaultRuleSyntax` or per-router `ruleSyntax` settings are required. +### nginx-proxy + acme-companion (compose.nginxproxy\*.yaml) + +| Variable | Purpose | Default | When to Set | +| ------------------- | ----------------------------------------- | ------- | ------------------------------------------ | +| `LETSENCRYPT_EMAIL` | Email for Let's Encrypt certificate | - | Required for `compose.nginxproxy-ssl.yaml` | +| `NGINX_PROXY_HOSTS` | Comma-separated hostnames for nginx-proxy | - | Required for `compose.nginxproxy*.yaml` | + +**Example:** + +```bash +NGINX_PROXY_HOSTS=example.com,www.example.com +``` + +> Note: Automatic certificates require port 80 to be reachable (HTTP-01). + +### Published Ports (Traefik and nginx-proxy) + +| Variable | Purpose | Default | When to Set | +| -------------------- | -------------------- | ------------------------------- | ---------------------------- | +| `HTTP_PUBLISH_PORT` | Published HTTP port | `80` (proxy) / `8080` (noproxy) | Change if port is in use | +| `HTTPS_PUBLISH_PORT` | Published HTTPS port | `443` | Change if port 443 is in use | + --- ## Site Configuration @@ -94,13 +118,12 @@ If your site is named `example.com` and you access it via that domain, no need t --- -## Nginx Proxy Configuration +## Frontend Nginx Configuration (inside the frontend container) | Variable | Purpose | Default | Allowed Values | | ---------------------- | ---------------------------------- | -------------- | -------------------------------------------- | | `BACKEND` | Backend service address and port | `0.0.0.0:8000` | `{host}:{port}` | | `SOCKETIO` | Socket.IO service address and port | `0.0.0.0:9000` | `{host}:{port}` | -| `HTTP_PUBLISH_PORT` | Published HTTP port | `8080` | Any available port | | `PROXY_READ_TIMEOUT` | Upstream request timeout | `120s` | Any nginx timeout value (e.g., `300s`, `5m`) | | `CLIENT_MAX_BODY_SIZE` | Maximum upload file size | `50m` | Any nginx size value (e.g., `100m`, `1g`) | diff --git a/docs/02-setup/05-overrides.md b/docs/02-setup/05-overrides.md index f1e274b1..0d7b6004 100644 --- a/docs/02-setup/05-overrides.md +++ b/docs/02-setup/05-overrides.md @@ -4,24 +4,26 @@ Overrides extend the base compose.yaml with additional services or modify existi docker compose -f compose.yaml -f overrides/compose.mariadb.yaml -f overrides/compose.redis.yaml config > compose.custom.yaml ``` -| Overrider | Purpose | Additional Info | -| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- | -| **Database** | | | -| compose.mariadb.yaml | Adds MariaDB database service | set `DB_PASSWORD` or default Password will be used | -| compose.mariadb-secrets.yaml | Adds MariaDB with password from a secret file instead of environment variable | Set `DB_PASSWORD_SECRETS_FILE` to the path of your secret file | -| compose.mariadb-shared.yaml | Makes MariaDB available on a shared network (mariadb-network) for other services | set `DB_PASSWORD` | -| compose.postgres.yaml | Uses PostgreSQL instead of MariaDB as the database | set `DB_PASSWORD` | -| **Proxy** | | | -| compose.noproxy.yaml | Exposes the application directly on port `:8080` without a reverse proxy | | -| compose.proxy.yaml | Uses Traefik as HTTP reverse proxy on port `:80` | You can change the published port by setting `HTTP_PUBLISH_PORT` | -| compose.https.yaml | Uses Traefik as HTTPS reverse proxy on Port `:443` with automatic HTTP-to-HTTPS redirect | `SITES_RULE` and `LETSENCRYPT_EMAIL` must be set. `HTTP_PUBLISH_PORT` and `HTTPS_PUBLISH_PORT` can be set. | -| **Redis** | | | +| 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 | | **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 | | | +| compose.backup-cron.yaml | | | +| compose.custom-domain-ssl.yaml | | | +| compose.custom-domain.yaml | | | +| compose.multi-bench-ssl.yaml | | | +| compose.multi-bench.yaml | | | diff --git a/docs/02-setup/08-single-server-nginxproxy-example.md b/docs/02-setup/08-single-server-nginxproxy-example.md new file mode 100644 index 00000000..ec9ef913 --- /dev/null +++ b/docs/02-setup/08-single-server-nginxproxy-example.md @@ -0,0 +1,177 @@ +# 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 (recommended: 2 vCPU, 4 GB RAM, 50 GB SSD). +- A public domain with DNS control. +- Two subdomains pointing to your server IP (A/AAAA records): + - `erp.your-domain.com` + - `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 +``` + +Generate the BASE64 value and build: + +```shell +export APPS_JSON_BASE64=$(base64 -w 0 ~/gitops/apps.json) + +docker build \ + --build-arg=FRAPPE_PATH=https://github.com/frappe/frappe \ + --build-arg=FRAPPE_BRANCH=version-16 \ + --build-arg=APPS_JSON_BASE64=$APPS_JSON_BASE64 \ + --tag=my-erpnext-prod-image:16.0.0 \ + --file=images/layered/Containerfile . +``` + +If `base64 -w 0` is not available on your system, use: + +```shell +export APPS_JSON_BASE64=$(base64 ~/gitops/apps.json | tr -d '\n') +``` + +### Configure environment + +Create an environment file for the bench: + +```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 erpnext --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) diff --git a/docs/03-production/01-tls-ssl-setup.md b/docs/03-production/01-tls-ssl-setup.md index 07a0787b..b88fc8c1 100644 --- a/docs/03-production/01-tls-ssl-setup.md +++ b/docs/03-production/01-tls-ssl-setup.md @@ -1,25 +1,53 @@ -# Accessing ERPNext through https on local deployment +# TLS/SSL Setup Overview -- ERPNext container deployment can be accessed through https easily using Caddy web server, Caddy will be used as reverse proxy and forward traffics to the frontend container. +Frappe Docker supports multiple TLS/SSL approaches. Choose the one that matches your routing needs and where you want the proxy to run. -### Prerequisites +## Options -- Caddy -- Adding a domain name to hosts file +### Traefik (built-in HTTPS) -#### Installation of caddy webserver +- 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) -- 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 +#### Traefik deployment models -```js -erp.localdev.net { - tls internal +- **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 - reverse_proxy localhost:8085 { +### 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) -- Caddy's root certificate must be added to other computers if computers from different networks access the ERPNext through https. +## 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 diff --git a/docs/03-production/04-nginx-proxy-acme-companion.md b/docs/03-production/04-nginx-proxy-acme-companion.md new file mode 100644 index 00000000..15c474eb --- /dev/null +++ b/docs/03-production/04-nginx-proxy-acme-companion.md @@ -0,0 +1,82 @@ +# 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 -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 -f ~/gitops/docker-compose.yml logs -f nginx-proxy +docker compose --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). diff --git a/docs/03-production/05-caddy-https.md b/docs/03-production/05-caddy-https.md new file mode 100644 index 00000000..a8683c5d --- /dev/null +++ b/docs/03-production/05-caddy-https.md @@ -0,0 +1,44 @@ +# 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 -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). diff --git a/docs/07-troubleshooting/02-windows-nginx-entrypoint-error.md b/docs/07-troubleshooting/02-windows-nginx-entrypoint-error.md index 7be1c2ca..112ed0ea 100644 --- a/docs/07-troubleshooting/02-windows-nginx-entrypoint-error.md +++ b/docs/07-troubleshooting/02-windows-nginx-entrypoint-error.md @@ -8,5 +8,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 ``` diff --git a/docs/getting-started.md b/docs/getting-started.md index f87b8617..e67f7415 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -93,8 +93,15 @@ Docker Compose "overrides" that extend the base compose.yaml for different scena - **compose.mariadb.yaml** - Adds MariaDB database service - **compose.redis.yaml** - Adds Redis caching service -- **compose.proxy.yaml** - Adds Traefik reverse proxy for multi-site hosting -- **compose.https.yaml** - Adds SSL/TLS certificate management +- **compose.proxy.yaml** - Adds Traefik reverse proxy for multi-site hosting (label-based routing) +- **compose.https.yaml** - Adds Traefik HTTPS + automatic certs (uses `SITES_RULE`) +- **compose.nginxproxy.yaml** - Adds nginx-proxy reverse proxy (HTTP, env-based `VIRTUAL_HOST`) +- **compose.nginxproxy-ssl.yaml** - Adds nginx-proxy + acme-companion (HTTPS, env-based `LETSENCRYPT_HOST`) + +**Proxy choice:** + +- Traefik is more flexible for advanced routing and multi-bench setups +- nginx-proxy is simpler for a single bench with host-based routing. ### 📁 development/ - Dev Environment @@ -103,8 +110,8 @@ Docker Compose "overrides" that extend the base compose.yaml for different scena ### 📁 resources/ - Runtime Templates -- **nginx-entrypoint.sh** - Dynamic Nginx configuration generator script -- **nginx-template.conf** - Nginx configuration template with variable substitution +- **core/nginx/nginx-entrypoint.sh** - Dynamic Nginx configuration generator script +- **core/nginx/nginx-template.conf** - Nginx configuration template with variable substitution ## Custom Apps Explained diff --git a/example.env b/example.env index e70e5864..56550219 100644 --- a/example.env +++ b/example.env @@ -29,6 +29,9 @@ FRAPPE_SITE_NAME_HEADER= # Default value is `8080`. HTTP_PUBLISH_PORT= +# Default value is `443`. +HTTPS_PUBLISH_PORT= + # Default value is `127.0.0.1`. Set IP address as our trusted upstream address. UPSTREAM_REAL_IP_ADDRESS= @@ -49,8 +52,14 @@ PROXY_READ_TIMEOUT= # Necessary if the upload limit in the frappe application is increased CLIENT_MAX_BODY_SIZE= +# Only with traefik overrides # Single site: SITES_RULE=Host(`erp.example.com`) # Multiple sites: SITES_RULE=Host(`a.example.com`) || Host(`b.example.com`) # More https://doc.traefik.io/traefik/routing/routers/#rule # About acme https://doc.traefik.io/traefik/https/acme/#domain-definition SITES_RULE=Host(`erp.example.com`) + +# Only with nginxproxy overrides +# Single site: NGINX_PROXY_HOSTS=erp.example.com +# Multiple sites: NGINX_PROXY_HOSTS=erp.example.com,www.erp.example.com +NGINX_PROXY_HOSTS=erp.example.com diff --git a/images/custom/Containerfile b/images/custom/Containerfile index 06182d42..21523892 100644 --- a/images/custom/Containerfile +++ b/images/custom/Containerfile @@ -2,8 +2,8 @@ ARG PYTHON_VERSION=3.11.6 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 ARG WKHTMLTOPDF_VERSION=0.12.6.1-3 ARG WKHTMLTOPDF_DISTRO=bookworm diff --git a/images/production/Containerfile b/images/production/Containerfile index 563bc763..d44f3878 100644 --- a/images/production/Containerfile +++ b/images/production/Containerfile @@ -69,8 +69,8 @@ RUN useradd -ms /bin/bash frappe \ && 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 FROM base AS build diff --git a/overrides/compose.nginxproxy-ssl.yaml b/overrides/compose.nginxproxy-ssl.yaml new file mode 100644 index 00000000..d8e66ead --- /dev/null +++ b/overrides/compose.nginxproxy-ssl.yaml @@ -0,0 +1,26 @@ +services: + frontend: + environment: + LETSENCRYPT_HOST: ${NGINX_PROXY_HOSTS:?No NGINX_PROXY_HOSTS set} + + nginx-proxy: + 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: diff --git a/overrides/compose.nginxproxy.yaml b/overrides/compose.nginxproxy.yaml new file mode 100644 index 00000000..f1f4ebf5 --- /dev/null +++ b/overrides/compose.nginxproxy.yaml @@ -0,0 +1,21 @@ +services: + frontend: + environment: + VIRTUAL_HOST: ${NGINX_PROXY_HOSTS:?No NGINX_PROXY_HOSTS set} + VIRTUAL_PORT: 8080 + + nginx-proxy: + image: nginxproxy/nginx-proxy:alpine + restart: unless-stopped + ports: + - ${HTTP_PUBLISH_PORT:-80}:80 + volumes: + - nginx-proxy-certs:/etc/nginx/certs + - nginx-proxy-html:/usr/share/nginx/html + - nginx-proxy-vhost:/etc/nginx/vhost.d + - /var/run/docker.sock:/tmp/docker.sock:ro + +volumes: + nginx-proxy-certs: + nginx-proxy-html: + nginx-proxy-vhost: diff --git a/resources/nginx-entrypoint.sh b/resources/core/nginx/nginx-entrypoint.sh similarity index 100% rename from resources/nginx-entrypoint.sh rename to resources/core/nginx/nginx-entrypoint.sh diff --git a/resources/nginx-template.conf b/resources/core/nginx/nginx-template.conf similarity index 100% rename from resources/nginx-template.conf rename to resources/core/nginx/nginx-template.conf