A Synology NAS makes a surprisingly good little container host: it's always on, it has plenty of storage, and DSM handles RAID, snapshots and backups for you. Since DSM 7.2, Docker comes as the Container Manager package, and its Project feature runs standard Docker Compose files. (On some newer DSM builds the package name has drifted back towards plain "Docker", so check Package Center for whichever one your model offers.)
This guide is the setup I'd recommend to anyone starting out: a tidy folder layout, permissions that don't break, a first Compose project, sensible ports, and an update and backup routine. I'll use Uptime Kuma as the example because it's small, useful and has a web UI.
Check your model first
Container Manager runs on most x86 "Plus" and "XS" models and on some ARM64 models. Budget "j" and value-series units often can't run it at all. Check your model's page on Synology's site or search for the package in Package Center. If it isn't listed, your NAS can't run it.
RAM matters more than CPU for typical home-lab containers. 2 GB is tight once DSM, Synology Photos and a few containers are running; 4 GB or more is comfortable.
Step 1: Create a docker shared folder
Installing Container Manager usually creates a shared folder called docker on your first volume. If not, create it in Control Panel > Shared Folder. Give each project its own subfolder:
/volume1/docker/
├── uptime-kuma/
│ ├── compose.yaml
│ └── data/
└── wiki/
├── compose.yaml
├── .env
└── data/
Keeping the Compose file next to its data makes each app self-contained, easy to back up and easy to move to another machine.
Step 2: Work out PUID and PGID
The most common Synology container problem is permissions: the app inside the container runs as a user that can't write to the folder you mapped. Many images (especially LinuxServer.io ones) accept PUID and PGID variables so the container runs as a real DSM user.
Create a dedicated, non-admin DSM user for containers (for example dockerapps), give it read/write access to the docker share, then find its IDs over SSH:
id dockerapps # uid=1027(dockerapps) gid=100(users) groups=100(users)
Images that don't support PUID/PGID usually document a fixed user ID instead. For those, give that UID ownership of the data folder once over SSH (for example sudo chown -R 1000:1000 /volume1/docker/app/data).
Step 3: Write the Compose file
Create /volume1/docker/uptime-kuma/compose.yaml. File Station works, but editing over SSH with vi or VS Code Remote is easier (see my Synology SSH key guide).
services:
uptime-kuma:
image: louislam/uptime-kuma:1
container_name: uptime-kuma
restart: unless-stopped
ports:
- "127.0.0.1:3001:3001"
volumes:
- ./data:/app/data
environment:
- TZ=Australia/Melbourne
Three habits worth copying:
- Pin a major version tag (
:1) rather than:latest, so an update never silently jumps to a new major release with breaking changes. - Use relative paths (
./data) so the project folder is portable. - Bind to
127.0.0.1when the app will sit behind DSM's reverse proxy. It's then unreachable directly from the LAN, and only the proxy can reach it. Use"3001:3001"instead if you want LAN access by IP and port.
Step 4: Create the project in Container Manager
- Open Container Manager > Project > Create.
- Name it
uptime-kumaand set the path to/docker/uptime-kuma. - Choose Use existing docker-compose.yml (it detects
compose.yaml), or paste the file into the editor. - Skip the Web Station portal option for now; we'll use the reverse proxy instead.
- Click Done. Container Manager pulls the image and starts the stack.
The project page shows logs, lets you stop, start and rebuild the stack, and edit the YAML. Behind the scenes it's plain Docker Compose, so you can also manage it over SSH:
cd /volume1/docker/uptime-kuma sudo docker compose ps sudo docker compose logs -f --tail=50
On DSM, Docker commands need sudo because the Docker socket belongs to root. Don't add users to a docker group or loosen the socket permissions; that's equivalent to giving them root.
Step 5: Avoid port conflicts
DSM's own web server uses ports 80 and 443, and DSM itself uses 5000 and 5001. Don't map containers to those. Pick high ports (3000–9999 is fine, avoiding anything another package already uses) and check what's listening before you choose:
sudo netstat -tlnp | grep -E ':(3001|8080|8443) '
To give apps proper HTTPS names on port 443, put them behind DSM's reverse proxy. That whole process is covered in Synology reverse proxy with Let's Encrypt.
Step 6: Keep secrets out of the YAML
For apps that need database passwords or API keys, put them in a .env file beside compose.yaml and reference them:
# .env (chmod 600, never commit to git) DB_PASSWORD=change-me-to-a-long-random-string
environment:
- DB_PASSWORD=${DB_PASSWORD}
Generate strong values with openssl rand -base64 32. For a deeper look at secrets, named volumes and networks, read Docker in production: volumes, networks and Compose secrets.
Step 7: Updating containers
Container Manager can show when a newer image is available. The simplest manual routine over SSH:
cd /volume1/docker/uptime-kuma sudo docker compose pull sudo docker compose up -d sudo docker image prune -f
pull downloads newer images for your pinned tags, up -d recreates only the containers whose image changed, and prune removes the old images. Read the app's release notes before major-version jumps, and take a snapshot first (next step). Avoid auto-updaters that pull :latest overnight on anything you rely on.
Step 8: Snapshots and backups
This is where a Synology shines:
- Snapshots: if your volume is Btrfs, install Snapshot Replication and schedule hourly or daily snapshots of the
dockershare. A bad update becomes a two-minute rollback. (Btrfs snapshots are copy-on-write, so they're nearly free until data changes.) - Backups: add the
dockershare to a Hyper Backup task that goes off the box, to a USB drive, another NAS or cloud storage. Snapshots on the same disks are not a backup. - Databases: for apps with a database container, stop the stack or use the database's own dump tool before backing up, so you never copy a half-written database file.
Troubleshooting
- Container restarts in a loop: check
sudo docker compose logs. "Permission denied" on a data path means PUID/PGID or folder ownership (Step 2). - "Port is already allocated": something else is on that port; pick another one (Step 5).
- App works by IP but not through the proxy: the reverse proxy destination port is wrong, or the app needs WebSocket headers.
- Container can't reach the internet: check the DSM firewall and that the container is on the default bridge or a user-defined network, not
network_mode: none.
Next steps
Once one project works, the pattern repeats: a folder, a Compose file, a high port and a reverse proxy rule. If you want rootless containers with tighter isolation on a regular Linux server, have a look at Podman rootless containers.
Comments (0)
No comments yet. Be the first to leave one!
Leave a Comment