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:
@@ -717,6 +717,7 @@ One line per decision: `[YYYY-MM-DD] <decision>: <why>`. Appended by agents and
|
||||
[2026-07-30] Report-vocabulary synonyms live in ReportDescriptor.searchTerms, NOT in the command-palette keywords, when they are words another report already owns. "stäm av"/"avstämning" on the huvudbok palette entry hijacked Enter from Bankavstämning, because the palette auto-selects the first hit and huvudbok is listed above it. The library search shows a list and has no such failure mode, so broad task-vocabulary belongs there.
|
||||
[2026-08-01] Palette is persisted as an independent root data attribute instead of new next-themes names: Light, Dark, and System values remain backward-compatible, and System can still follow the OS while each palette supplies both light and dark tokens.
|
||||
[2026-08-01] Palette refinement increases hue in primary, frame, secondary, muted, and border tokens only: Indigo, Forest, and Sand read more clearly without recoloring success, warning, destructive, or chart semantics.
|
||||
[2026-08-01] Accounted's base self-hosted Compose omits `cpus` and `healthcheck.start_interval`: both controls are optional, bundled NAS Compose parsers can reject them, and CPU policy belongs in host tooling or a local override. The version-controlled `docker-compose.resources.yml` overlay retains the former two-CPU cap and faster startup probing for command-line Compose 2.20.2+ without breaking the portable base. Existing operators are told to reapply the cap through either path. Supabase bind mounts, database ports, and JWKS syntax remain version-matched upstream concerns, with scoped NAS guidance in Accounted's integration docs rather than a forked Supabase Compose file; the guide loopback-binds both upstream Supavisor ports by default because changing 5432 alone resolves a collision but does not prevent LAN exposure.
|
||||
|
||||
[2026-07-30] A missing org number on either side of the Bokio connect probe does NOT block the connection; only a confident mismatch does. Accounted allows companies without an org number and a provider response can omit it, so blocking on absence would refuse legitimate connections to prevent a mismatch we have no evidence of. Absence instead falls through to labelling the consent with the company the credentials actually opened, which is what lets the user catch it. Same reasoning applied to keeping 429/5xx from the probe out of the invalid-credentials mapping: a provider outage must not read as "your token is wrong".
|
||||
|
||||
@@ -727,3 +728,4 @@ One line per decision: `[YYYY-MM-DD] <decision>: <why>`. Appended by agents and
|
||||
[2026-07-31] Assistant product knowledge REVERTED before merge (founder call): the assistant gets no internal knowledge of Accounted's own features yet. The working product-tier implementation (agent_atom_registry CHECK widening, product/bokforingsmallar atom, discovery, MCP + panel wiring, all CI green at bb65224c) lives in fix/mall-line-type-clarity branch history and a follow-up issue for when it is wanted. Only the UI clarification ships for the radtyp confusion: unified "Kostnad/Intäkt" label and an InfoTooltip explaining the three radtyper at the point of choice.
|
||||
|
||||
[2026-08-01] Cloud Backup OAuth redirect URIs resolve from NEXT_PUBLIC_APP_URL, with request origin only as a self-hosted fallback: Google and Dropbox require pre-registered callbacks, so deriving them from an old alias or preview host can reject the flow before consent; one resolver keeps authorization, exchange, revoke, and sync origins consistent.
|
||||
[2026-08-01] Do not claim a fixed Supabase Docker revision: Accounted does not maintain a tested Supabase stack pin, so the self-hosting guide requires one complete immutable upstream tag or commit instead of naming an unverified version.
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
# Optional resource and startup-health controls.
|
||||
# Requires Docker Compose 2.20.2+ and Docker Engine 25.0+.
|
||||
# Usage: docker compose -f docker-compose.yml -f docker-compose.resources.yml up -d
|
||||
services:
|
||||
app:
|
||||
cpus: 2
|
||||
healthcheck:
|
||||
start_interval: 5s
|
||||
+4
-2
@@ -26,8 +26,9 @@ services:
|
||||
- /tmp
|
||||
- /app/.next:uid=1001,gid=1001,mode=750,size=400m
|
||||
- /app/public:uid=1001,gid=1001,mode=750,size=200m
|
||||
# CPU limits are intentionally left to host tooling or a local override.
|
||||
# Some Compose builds bundled with NAS platforms reject the `cpus` key.
|
||||
mem_limit: 1g
|
||||
cpus: 2
|
||||
pids_limit: 200
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
@@ -38,7 +39,8 @@ services:
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
start_period: 30s
|
||||
start_interval: 5s
|
||||
# `start_interval` requires Docker Compose 2.20.2+ and Docker Engine 25.0+.
|
||||
# Omitting it keeps the base file usable with older NAS container stacks.
|
||||
retries: 3
|
||||
logging:
|
||||
driver: json-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.
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user