docs(docker): improve NAS self-hosting compatibility (#1330)

* docs(docker): improve NAS self-hosting compatibility

Signed-off-by: Emil <emilmattsson14@gmail.com>

* docs(docker): retain optional resource controls

Signed-off-by: Emil <emilmattsson14@gmail.com>

* docs(docker): clarify compatibility prerequisites

---------

Signed-off-by: Emil <emilmattsson14@gmail.com>
This commit is contained in:
Mattsson
2026-08-01 18:54:46 +02:00
committed by GitHub
parent bfbd926950
commit 115147d304
5 changed files with 175 additions and 2 deletions
+61
View File
@@ -67,6 +67,67 @@ curl http://localhost:3000/api/health
---
## Synology DSM and Xpenology
Use a **Container Manager Project**, not the single-container wizard. The
project path is the working directory for every relative path in the Compose
file. Create that directory first and put all of these files in it before
deploying the project:
```text
docker-compose.yml
.env
docker/
cron.Dockerfile
crontab.self-hosted
```
Uploading only `docker-compose.yml` is not enough: the cron service is built
from `docker/cron.Dockerfile` and bind-mounts
`docker/crontab.self-hosted`. Keep `.env` readable only by the administrator
and Container Manager because it contains the Supabase service-role key.
Container Manager ships its own Compose build, and supported keys vary by DSM
release. Accounted's base Compose file avoids the optional `cpus` and
`healthcheck.start_interval` keys for compatibility. Set a CPU limit through
Container Manager's resource controls or a local override if needed. When
updating an existing deployment that relied on the previous two-CPU cap,
reapply that limit in the host controls before restarting the project.
If you run Docker Compose 2.20.2 or newer against Docker Engine 25.0 or newer,
the optional resource overlay restores the previous two-CPU cap and faster
startup health checks while keeping the base file compatible. Download the
overlay from the same Accounted tag or full commit as the base Compose file:
```bash
ACCOUNTED_REF=replace-with-the-same-tag-or-full-commit
curl -fsSLo docker-compose.resources.yml \
"https://raw.githubusercontent.com/erp-mafia/accounted/${ACCOUNTED_REF}/docker-compose.resources.yml"
docker compose -f docker-compose.yml -f docker-compose.resources.yml up -d
```
Compose only applies the files named in each invocation. Keep the resource
overlay in every later `up` command, after any other overlay. For example:
```bash
# HTTPS with Caddy
docker compose -f docker-compose.yml -f docker-compose.caddy.yml -f docker-compose.resources.yml up -d
# Local image build
docker compose -f docker-compose.yml -f docker-compose.build.yml -f docker-compose.resources.yml up --build -d
```
Do not use this overlay if Container Manager rejects either key or the Docker
Engine is older than 25.0. The memory and PID limits remain active in the base
file either way.
Accounted itself does not use PostgreSQL port 5432 and does not need a database
data folder when connected to Supabase Cloud. If Supabase is also running on
the NAS, follow the [fully self-hosted notes](SELF-HOSTING.md#synology-dsm-and-xpenology-notes)
for its separate project, bind mounts, ports, and JWKS configuration.
---
## Enable HTTPS (recommended)
Ship a Caddy reverse proxy alongside the app: it auto-provisions Let's Encrypt certificates and renews them forever.
+100
View File
@@ -350,6 +350,106 @@ flowchart LR
5. **Reverse proxy** in front of both hosts. The app container and the Supabase `kong` container must share an external Docker network so the proxy can route to them by name.
### Synology DSM and Xpenology notes
Run Accounted and Supabase as two separate Container Manager Projects with two
separate project directories. Accounted owns the Compose files in this
repository. Supabase owns its database, Auth, Realtime, Storage, and pooler
configuration. Choose one upstream release tag or full commit and copy the
complete `docker/` directory from that immutable revision, following the
[official Supabase Docker guide](https://supabase.com/docs/guides/self-hosting/docker).
Do not copy individual snippets into Accounted's Compose file or mix files from
different upstream revisions.
For the **Accounted project**, follow the
[Accounted Container Manager file layout](DOCKER.md#synology-dsm-and-xpenology).
For the **Supabase project**:
1. Copy the entire upstream `supabase/docker/` directory into the project
directory. Do not upload only its `docker-compose.yml`: it bind-mounts SQL,
gateway, function, pooler, and Storage files from the accompanying
`volumes/` tree.
2. Create the two runtime directories that upstream deliberately excludes from
Git before the first deployment. File Station is fine, or from the Supabase
project directory use:
```bash
mkdir -p volumes/db/data volumes/storage
```
Container Manager must be able to write to both directories. Use the
narrowest NAS ACL that works for the container runtime; do not make the
whole shared folder world-writable.
3. Supavisor publishes two host ports. Before starting the project, make sure
both `POSTGRES_PORT` and `POOLER_PROXY_PORT_TRANSACTION` in the **Supabase**
`.env` are unused on the NAS. If the defaults conflict, examples are
`POSTGRES_PORT=5433` for session mode and
`POOLER_PROXY_PORT_TRANSACTION=6544` for transaction mode. Changing only
`POSTGRES_PORT` does not resolve a conflict on the transaction port.
Accounted's `PORT` only changes the web app port and cannot resolve either
database conflict. Do not expose the Supabase `db` container directly just
to solve a conflict: the official stack exposes PostgreSQL through
Supavisor.
Supabase's default Supavisor port mappings listen on every host interface.
Accounted does not need either database port over the network, so on a
shared NAS bind both mappings to loopback in the version-matched Supabase
Compose file:
```yaml
services:
supavisor:
ports:
- "127.0.0.1:${POSTGRES_PORT}:5432"
- "127.0.0.1:${POOLER_PROXY_PORT_TRANSACTION}:6543"
```
If another trusted machine must connect, bind to a specific private NAS
address and restrict both ports to trusted source addresses in the DSM
firewall. Never forward either database port to the public internet.
4. The default Accounted integration uses Supabase's legacy `ANON_KEY` and
`SERVICE_ROLE_KEY`, so asymmetric keys and `JWT_JWKS` are optional. Leave
the upstream JWKS lines commented when using legacy-only mode. If you enable
Supabase's new asymmetric keys, generate them with the upstream
`utils/add-new-auth-keys.sh` script and follow the
[official authentication-key guide](https://supabase.com/docs/guides/self-hosting/self-hosted-auth-keys).
Some older Compose parsers reject the inline JSON fallback in Supabase's
optional Realtime setting:
```yaml
API_JWT_JWKS: ${JWT_JWKS:-{"keys":[]}}
```
After `JWT_JWKS` has been generated and saved in the Supabase `.env`, use the
direct substitution documented by Supabase for limited Compose parsers:
```yaml
# Supabase PostgREST
PGRST_JWT_SECRET: ${JWT_JWKS}
# Supabase Realtime
API_JWT_JWKS: ${JWT_JWKS}
# Supabase Storage
JWT_JWKS: ${JWT_JWKS}
```
Do not invent an empty or placeholder JWKS for a production deployment. Either
keep asymmetric authentication disabled or configure the generated value
consistently for every Supabase service that verifies tokens.
Accounted's base Compose file intentionally omits the optional `cpus` and
`healthcheck.start_interval` settings because older Container Manager Compose
builds can reject them. Operators who need a CPU cap can set one through DSM's
resource controls or a local Compose override. Existing deployments that
relied on the previous two-CPU cap must reapply it before restarting with the
new base file. Command-line deployments on Docker Compose 2.20.2 or newer and
Docker Engine 25.0 or newer can use the version-controlled
`docker-compose.resources.yml` overlay to restore both the cap and faster
startup health checks; older NAS container stacks should keep using the
portable base file alone.
### What you give up vs. cloud Supabase
- **Backups** are entirely your responsibility: set up `pg_dump` (or a tool like restic) to off-host storage. As a portable, vendor-neutral *logical* backup on top of the raw dump, you can also export each fiscal period as a standard **SIE4** file via the API and archive it: any Swedish bookkeeping system can re-import it: