Make Coolify service ports configurable

This commit is contained in:
2026-08-17 01:14:39 -04:00
parent 24a1307a96
commit dafa62b9bb
4 changed files with 75 additions and 57 deletions
+18 -3
View File
@@ -68,6 +68,20 @@ DB_DISABLE_SSL=true
POSTGRES_PORT=5432
GARAGE_API_PORT=3900
# Coolify Compose container listener ports. APP_PORT is Coolify-only;
# POSTGRES_PORT and GARAGE_API_PORT are reused from above. The regular/dev
# compose files keep the database and Garage container ports at 5432/3900 and
# use those variables only for host-side mappings.
APP_PORT=3000
GARAGE_RPC_PORT=3901
GARAGE_WEB_PORT=3902
GARAGE_ADMIN_PORT=3903
# Garage requires a 64-character hexadecimal RPC secret. Generate with:
# openssl rand -hex 32
# GARAGE_RPC_SECRET=
# GARAGE_ADMIN_TOKEN=
# GARAGE_METRICS_TOKEN=
# Optional: if Next dev picks another port, you do not need to change URLs for
# sign-in — the auth client uses window.location.origin in the browser.
@@ -138,7 +152,8 @@ NEXT_PUBLIC_UMAMI_SCRIPT_URL=https://analytics.umami.is/script.js
# • Coolify — see docs/COOLIFY.md. Summary:
# - Best: one Compose resource with docker-compose.coolify.yml (app+db+garage).
# - Application + separate Garage: ENOTFOUND garage → set S3_ENDPOINT to
# SERVICE_URL_GARAGE_3900 (public domain) OR http://garage-<resource-uuid>:3900
# SERVICE_URL_GARAGE (public domain) OR
# http://garage-<resource-uuid>:<GARAGE_API_PORT>
# with Connect to Predefined Network on both resources. Never bare "garage".
# - NEVER use localhost in production — inside the app container that is the app, not Garage.
#
@@ -150,8 +165,8 @@ S3_SECRET_KEY=7d37d093435a41f2aab8f13c19ba067d9776c90215f56614adad6ece597dbb34
S3_REGION=garage
# S3_FORCE_PATH_STYLE=true # default on when S3_ENDPOINT is set; required for Garage/HTTPS proxy
#
# docker-compose.yml sets S3_ENDPOINT=http://garage:3900 inside the app container
# automatically. S3_ACCESS_KEY / S3_SECRET_KEY must match the garage service env.
# docker-compose.yml uses http://garage:3900 internally. The Coolify compose
# derives the internal URL from GARAGE_API_PORT. Credentials must match Garage.
# =============================================================================
# SSO — Authentik OIDC (optional)
+19 -18
View File
@@ -6,16 +6,16 @@ beenvoice stores receipt files in S3-compatible storage when `S3_BUCKET`, `S3_AC
Docker DNS resolves service names **only inside the same Docker network**.
| Setup | Does `http://garage:3900` work? |
| Setup | Does `http://garage:<GARAGE_API_PORT>` work? |
|-------|--------------------------------|
| Single Compose stack (app + garage together) | Yes — Compose service name `garage` |
| beenvoice **Application** + Garage **separate Compose** | **No** — each resource has its own network by default |
| Application + Garage with shared destination network + correct hostname | Yes — hostname is usually **`garage-<resource-uuid>`**, not bare `garage` |
| Application + Garage via **public domain** (`SERVICE_URL_GARAGE_3900`) | Yes — no Docker DNS needed |
| Application + Garage via **public domain** (`SERVICE_URL_GARAGE`) | Yes — no Docker DNS needed |
Setting `S3_ENDPOINT=http://garage:3900` on a standalone beenvoice Application fails because the app container is not on the Garage stack's network. Node returns `ENOTFOUND garage`.
Setting `S3_ENDPOINT=http://garage:<GARAGE_API_PORT>` on a standalone beenvoice Application fails because the app container is not on the Garage stack's network. Node returns `ENOTFOUND garage`.
Also avoid `http://localhost:3900` inside the app container — that points at the app itself, not Garage.
Also avoid `http://localhost:<GARAGE_API_PORT>` inside the app container — that points at the app itself, not Garage.
---
@@ -27,9 +27,9 @@ Use this if you are **not** migrating to a single Compose stack today.
This is the most reliable fix when beenvoice is a Coolify **Application** (Dockerfile) and Garage is a separate Compose resource.
1. **Update the Garage stack** to the latest `docker-compose.coolify-garage.yml` from this repo (includes `SERVICE_FQDN_GARAGE_3900`) and **redeploy** the Garage resource.
2. In the **Garage Compose resource** → assign a domain for **port 3900** (e.g. `s3.yourdomain.com`). Coolify generates TLS via Traefik/Caddy.
3. Open the Garage resource **Environment** tab and copy **`SERVICE_URL_GARAGE_3900`** (e.g. `https://s3.yourdomain.com`).
1. **Update the Garage stack** to the latest `docker-compose.coolify-garage.yml` from this repo (includes `SERVICE_FQDN_GARAGE`) and **redeploy** the Garage resource.
2. In the **Garage Compose resource** → assign a domain to `GARAGE_API_PORT` (default **3900**, e.g. `s3.yourdomain.com`). Coolify generates TLS via Traefik/Caddy.
3. Open the Garage resource **Environment** tab and copy **`SERVICE_URL_GARAGE`** (e.g. `https://s3.yourdomain.com`).
4. On the **beenvoice Application** → Environment:
```env
@@ -55,12 +55,12 @@ Use when you want S3 API traffic to stay on the Docker network.
5. Set on beenvoice Application:
```env
S3_ENDPOINT=http://garage-<GARAGE_RESOURCE_UUID>:3900
S3_ENDPOINT=http://garage-<GARAGE_RESOURCE_UUID>:<GARAGE_API_PORT>
```
Example: resource UUID `k8w2o0g4s0g8` `S3_ENDPOINT=http://garage-k8w2o0g4s0g8:3900`.
Example with the default port and resource UUID `k8w2o0g4s0g8`: `S3_ENDPOINT=http://garage-k8w2o0g4s0g8:3900`.
**Do not use bare `garage`** unless you verified it resolves from inside the beenvoice container (recent Coolify versions may also register the short service name when both sides use Connect to Predefined Network — if `wget http://garage:3900` fails, use the `garage-<uuid>` form or Path A).
**Do not use bare `garage`** unless you verified it resolves from inside the beenvoice container (recent Coolify versions may also register the short service name when both sides use Connect to Predefined Network — if `wget http://garage:<GARAGE_API_PORT>` fails, use the `garage-<uuid>` form or Path A).
6. Match credentials and bucket:
@@ -79,10 +79,12 @@ Deploy the root **[`docker-compose.coolify.yml`](../../../docker-compose.coolify
1. Coolify → **New Resource****Docker Compose**
2. Point at this repo; compose file: **`docker-compose.coolify.yml`**
3. Set env vars from [`.env.example`](../.env.example): `AUTH_SECRET`, `POSTGRES_PASSWORD`, `S3_ACCESS_KEY`, `S3_SECRET_KEY`, etc.
3. Set env vars from [`.env.example`](../.env.example): `AUTH_SECRET`, `POSTGRES_PASSWORD`, `S3_ACCESS_KEY`, `S3_SECRET_KEY`, and a 64-character hexadecimal `GARAGE_RPC_SECRET` (`openssl rand -hex 32`). Garage has a valid development fallback, but production should override it.
4. Assign a domain to the **`app`** service (Coolify fills `SERVICE_URL_APP` / `BETTER_AUTH_URL` automatically).
5. **Do not** override `S3_ENDPOINT` — the compose file sets `S3_ENDPOINT=http://garage:3900` on the shared network.
6. Redeploy.
5. Optionally set `APP_PORT`, `POSTGRES_PORT`, `GARAGE_API_PORT`, `GARAGE_RPC_PORT`, `GARAGE_WEB_PORT`, or `GARAGE_ADMIN_PORT`. Defaults are `3000`, `5432`, and `3900``3903` respectively.
6. Only the app port is exposed to Coolify's proxy. PostgreSQL and every Garage listener remain reachable only through the stack's internal Docker network.
7. **Do not** override `S3_ENDPOINT` — the compose file derives it from `GARAGE_API_PORT` on the shared network.
8. Redeploy.
Alternative: [`docker-compose.yml`](../../../docker-compose.yml) works the same way; `docker-compose.coolify.yml` adds Coolify magic vars (`SERVICE_FQDN_APP`) and omits host port bindings for db/Garage.
@@ -95,7 +97,7 @@ Alternative: [`docker-compose.yml`](../../../docker-compose.yml) works the same
| Garage / MinIO compose | Remove after data migrated (rclone) or re-point receipts (new bucket) |
| Env vars | Move `AUTH_SECRET`, Resend, Authentik, etc. to the Compose resource env |
**Migrating from MinIO:** Garage uses port **3900** (not 9000) and Garage-format access keys (`GK…`). Update `S3_ENDPOINT`, `S3_REGION=garage`, and credentials. Receipt blobs in the old MinIO volume are not auto-migrated.
**Migrating from MinIO:** Garage defaults to port **3900** (not 9000) and uses Garage-format access keys (`GK…`). Update `S3_ENDPOINT`, `S3_REGION=garage`, and credentials. Receipt blobs in the old MinIO volume are not auto-migrated.
---
@@ -114,9 +116,8 @@ Do **not** add `networks: coolify: external: true` unless you know the exact ext
## Checklist (Application + separate Garage)
- [ ] Garage stack redeployed with current `docker-compose.coolify-garage.yml`
- [ ] **Path A:** domain on port 3900 + `S3_ENDPOINT` = `SERVICE_URL_GARAGE_3900`
**or Path B:** Connect to Predefined Network on **both** resources + `S3_ENDPOINT=http://garage-<uuid>:3900`
- [ ] `S3_ENDPOINT` is **not** `http://garage:3900`, **not** `localhost`
- [ ] **Path A:** domain targets `GARAGE_API_PORT` + `S3_ENDPOINT` = `SERVICE_URL_GARAGE`; **or Path B:** Connect to Predefined Network on **both** resources + `S3_ENDPOINT=http://garage-<uuid>:<GARAGE_API_PORT>`
- [ ] `S3_ENDPOINT` is not a bare `http://garage:<port>` across separate resources and is **not** `localhost`
- [ ] `S3_ACCESS_KEY` / `S3_SECRET_KEY` match the Garage stack env
- [ ] `S3_BUCKET` exists (Garage `--default-bucket` creates `beenvoice-receipts` on first start)
- [ ] Redeployed beenvoice after env or network changes
@@ -131,7 +132,7 @@ docker exec -it <beenvoice-container> sh
wget -qO- "https://s3.yourdomain.com" || curl -sf "https://s3.yourdomain.com"
# Path B — internal host from S3_ENDPOINT
wget -qO- "http://garage-<uuid>:3900" || curl -sf "http://garage-<uuid>:3900"
wget -qO- "http://garage-<uuid>:<GARAGE_API_PORT>" || curl -sf "http://garage-<uuid>:<GARAGE_API_PORT>"
```
If this fails with "bad address" or timeout, fix networking / `S3_ENDPOINT` before debugging app code. On first S3 use, the app logs a hint if DNS fails or if `S3_ENDPOINT` still uses bare `garage` in production.