Skip to content

UGREEN NAS Stack

Consolidated compose stack for the UGREEN NAS (UGREEN-NAS-MKP, 192.168.1.29, user unknown224).

Migrated from twelve independent compose projects that lived in /volume1/docker-ssd/<app>/ and /volume2/docker/<app>/, each with its own docker-compose.yaml and hardcoded secrets.

PathContents
docker-compose.ymlinclude: list — the only file compose is invoked on
host/arcane.yamlArcane agent, extends ../../arcane/arcane-agent.yaml
host/deunhealth.yamlWatchdog restarting unhealthy gluetun-namespace sharers
apps/<name>/<name>.yamlOne file per application

dozzle-agent comes from the shared ../dozzle/dozzle-agent.yaml.

ServicePort(s)Data location
gluetun21287, 21265, 6881-6882, 21478/home/unknown224/Apps/gluetun
gluetun-webui65258
qbittorrent1via gluetun (21287)/volume1/docker-ssd/qbittorrent/appdata
rustatiovia gluetun (21478)volume rustatio_data
qbitwebui26610/volume1/docker-ssd/qbitwebui/data
qui7476/volume1/docker-ssd/qui-bittorrent/qui
anibridge4848/volume1/docker-ssd/anibridge/data
metube8081/volume2/metube/{downloads,audio-downloads}
speedtest-tracker8899, 8793/volume1/docker-ssd/speedtest-tracker/config
upsnaphost network/volume1/docker-ssd/upsnap/data
dozzle-agent7007
arcane-agent3553volume arcane-agent-data
deunhealthnone
backup2none/volume2/docker-apps-backup (archives)

Every image is pulled through the internal Harbor at harbor.toolsera.lan, using the proxy-cache project that matches its upstream registry:

UpstreamHarbor project
Docker Hubdocker_proxy_cache
ghcr.ioghcr_proxy_cache
lscr.iolscr_proxy_cache

The registry host is stripped and the rest of the path is preserved, e.g. ghcr.io/autobrr/qui:latest becomes harbor.toolsera.lan/ghcr_proxy_cache/autobrr/qui:latest.

Harbor requires authentication (/v2/ returns 401), which the Docker daemon holds as root. Note that docker manifest inspect run as unknown224 fails with open /etc/docker/certs.d/harbor.toolsera.lan/ca.crt: permission denied — that is a client-side file-permission issue, not a registry problem. Pulls performed by the daemon are unaffected.

  • Secrets externalised. Every hardcoded credential became a ${VAR} placeholder documented in .env.example. Nothing in this directory carries a real value.
  • Relative bind mounts made absolute. ./data and ./qui resolved against each app’s old directory; consolidating the compose files into one project would have silently repointed them at new empty directories. They now name the existing paths explicitly, so state is preserved.
  • network_mode: service:gluetun on qbittorrent and rustatio, gated behind depends_on: condition: service_healthy, with orphan-detecting healthchecks and a deunhealth watchdog. See apps/qbittorrent/README.md — a gluetun restart silently strands both containers in a dead network namespace.
  • container_name added to anibridge and qbitwebui, which previously ran as anibridge-anibridge-1 / qbitwebui-qbitwebui-1.
  • version: keys dropped from qui and upsnap (obsolete in compose v2).
  • All images repointed at Harbor. See Registry.
  • dozzle-agent captured as a compose service via the shared ../dozzle/dozzle-agent.yaml; it was a bare docker run container.
  • gluetun’s control-server auth JSON fixed. The live container has HTTP_CONTROL_SERVER_AUTH_DEFAULT_ROLE='{"auth":...}' — the single quotes are literal, so the value is not parseable JSON and gluetun falls back to /gluetun/auth/config.toml. This stack passes valid JSON, which means apikey auth actually takes effect on the control server. gluetun-webui already sends the key, but anything else polling :8000 will start getting 401s.

firefox lives at /volume2/@appstore/com.ugreen.docker.firefox/ and is managed by the UGREEN app store, which owns its lifecycle and rewrites its compose file. Bringing it into this stack would fight the appliance. Left in place.

watchtower and portainer_agent are running on the NAS but are deliberately not part of this stack. They stay under whatever manages them today.

  1. Rotate every secret. The old compose files on the NAS committed the Arcane agent token, CyberGhost OpenVPN credentials, gluetun API key, qbitwebui encryption key and the speedtest-tracker APP_KEY in plaintext on disk.
  2. Confirm the Arcane manager URL. The shared arcane/arcane-agent.yaml points at http://192.168.1.175:3552; the NAS agent is currently pointed at http://192.168.1.178:3552. One of the two is stale.
  3. Named volumes get renamed by the project switch. arcane-agent_arcane-data and rustatio_rustatio_data become ugreen-nas_arcane-agent-data and ugreen-nas_rustatio_data. Both start empty — the Arcane agent will need re-pairing, and rustatio (currently exited) loses its /data. Copy the old volumes across first if that state matters.
  4. Stop the old projects first. Container names collide, so the twelve existing projects must be down before this one comes up.
Terminal window
cd /volume1/docker-ssd/infrastructure-toolkit/docker-compose-apps/ugreen-nas
bws run --access-token "$BWS_ACCESS_TOKEN" -- docker compose up -d
  1. https://github.com/markkpamy/infrastructure-toolkit/blob/main/docker-compose-apps/ugreen-nas/Pinned to an exact build and upgraded one-way — back up appdata before any version bump. See apps/qbittorrent/README.md.

  2. https://github.com/markkpamy/infrastructure-toolkit/blob/main/docker-compose-apps/ugreen-nas/Nightly config/state backup of every app above. Adding an app to this stack means adding its data path to apps/backup/backup.yaml — coverage is not automatic. See apps/backup/README.md for the restore procedure and the encryption-key constraint.