Running Guacamole on Synology NAS by Docker Installation

What You’ll Build and Why It Works
Running Guacamole on Synology NAS by Docker Installation gives you a clean, reproducible way to host Apache Guacamole—an HTML5 remote desktop gateway—on your NAS. You’ll run three containers: the Guacamole web app, the guacd
daemon (the protocol engine), and a MariaDB database to store users and connections. With Docker, everything stays tidy, portable, and easy to update. The provided docker-compose.yml
, .env
, and initdb.sql
shrink setup time and reduce errors, while Synology’s Container Manager (Docker) handles lifecycle and logs.
Architecture at a Glance (guacd, web app, MariaDB)
- guacd speaks RDP, VNC, and SSH for you.
- Guacamole web app serves the UI at
http://NAS_IP:8348/
(mapped from container port8080
). - MariaDB stores configuration, users, permissions, and connections.
- A private bridge network keeps services isolated and clean.

Why Synology + Docker Is Perfect for Guacamole
- Simple updates: pull the latest images and recreate.
- Clear persistence: bind mounts store DB data and Guacamole configs.
- Unified management: DSM UI, logs, and reverse proxy in one place.
Before You Start: Requirements & Assumptions
DSM, Container Manager, and SSH Access
- Synology DSM 7.x with Container Manager (formerly Docker) installed.
- Optional: SSH enabled for CLI operations (
Control Panel → Terminal & SNMP → Enable SSH
). - A shared folder where you’ll keep the stack (e.g.,
/volume1/docker/guacamole
).
Ports, Volumes, and File Paths on Synology
- The guide maps 8348 → 8080. You can change
8348
if needed. - Compose uses relative paths like
./mariadb
,./init
,./home
. On Synology, place your project folder in a shared location and run compose from there.
Project Layout: Folders You Need to Create
Directory Tree and Path Notes (bind mounts)
Create a project folder (example path):
/volume1/docker/guacamole/
├─ docker-compose.yml
├─ .env
├─ init/
│ └─ initdb.sql
├─ mariadb/ # MariaDB data (persistent)
└─ home/ # Guacamole home (extensions, branding, etc.)
mariadb/
must be writable by Docker.init/
containsinitdb.sql
executed once on first DB start.home/
becomesGUACAMOLE_HOME
where you can add extensions later.
Your Compose Stack
docker-compose.yml
# docker-compose.yml
# Purpose: Deploy Apache Guacamole on Synology via Docker Compose.
# Stack: MariaDB (database), guacd (protocol daemon), guacamole (web app).
# Note: Run `docker compose up -d` from this directory on your Synology (SSH or Container Manager).
networks:
guacamole-wody-kr-net:
driver: bridge # Isolated bridge network for clean service discovery and traffic separation
services:
db:
image: mariadb:lts # Stable MariaDB image (LTS tag)
container_name: guacamole-wody-kr-mariadb
restart: unless-stopped # Auto-restart unless explicitly stopped
environment:
MARIADB_DATABASE: db_guacamole # DB name for Guacamole
MARIADB_USER: user_guacamole # Application DB user
MARIADB_PASSWORD: ${MYSQL_USER_PASSWORD} # Loaded from .env (do not hardcode)
MARIADB_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} # Root password from .env
TZ: Asia/Seoul # Keep container time consistent with Synology
command:
- --max_allowed_packet=1073741824 # Allow large packets (1GiB) for file transfers
- --character-set-server=utf8mb4 # Full UTF-8 support
- --collation-server=utf8mb4_unicode_ci # Recommended collation
volumes:
- ./mariadb:/var/lib/mysql # Persist database data on the NAS
- ./init:/docker-entrypoint-initdb.d:ro # Run init scripts (e.g., initdb.sql) on first start
healthcheck:
# Check if MariaDB is ready by pinging localhost over TCP
test: ["CMD-SHELL", "mariadb-admin ping -h 127.0.0.1 --protocol=TCP --connect-timeout=5 --silent"]
interval: 15s
timeout: 10s
retries: 8
start_period: 90s
security_opt:
- no-new-privileges:true # Hardens the container against privilege escalation
networks:
- guacamole-wody-kr-net
guacd:
image: guacamole/guacd:latest # guacd daemon (RDP, SSH, VNC engine)
container_name: guacamole-wody-kr-guacd
restart: unless-stopped
healthcheck:
# Check that guacd is listening on port 4822
test: ["CMD-SHELL", "nc -z 127.0.0.1 4822 || exit 1"]
interval: 10s
timeout: 3s
retries: 10
security_opt:
- no-new-privileges:true
networks:
- guacamole-wody-kr-net
guacamole:
image: guacamole/guacamole:latest # Guacamole web application (Tomcat)
container_name: guacamole-wody-kr-app
depends_on:
db:
condition: service_healthy # Start only after DB is healthy
guacd:
condition: service_started # Wait until guacd is running
ports:
- "8348:8080" # Expose web UI on http://NAS_IP:8348/
environment:
TZ: Asia/Seoul
GUACD_HOSTNAME: guacd # Service name from this compose file
GUACD_PORT: 4822
MYSQL_HOSTNAME: db
MYSQL_PORT: 3306
MYSQL_DATABASE: db_guacamole
MYSQL_USERNAME: user_guacamole
MYSQL_PASSWORD: ${MYSQL_USER_PASSWORD}
WEBAPP_CONTEXT: "ROOT" # Serve UI at "/" (not /guacamole)
# REMOTE_IP_VALVE_ENABLED: "true" # Enable if you run behind reverse proxy and need client IPs
# PROXY_ALLOWED_IPS_REGEX: ".*" # Restrict to trusted proxy IPs if enabling Remote IP Valve
GUACAMOLE_HOME: /guac-home # Bind mount for extensions & branding
volumes:
- ./home:/guac-home # Persist Guacamole home on NAS
healthcheck:
# Simple HTTP check against Tomcat root context
test: ["CMD-SHELL", "wget -qO- http://127.0.0.1:8080/ || exit 1"]
interval: 15s
timeout: 5s
retries: 20
security_opt:
- no-new-privileges:true
networks:
- guacamole-wody-kr-net
.env
# .env
# Store secrets outside compose. Do NOT commit this file to public repos.
MYSQL_ROOT_PASSWORD=Your_Own_Strong_Password # MariaDB root password (administrator)
MYSQL_USER_PASSWORD=Your_Own_Strong_Password # Password for user_guacamole (application user)
initdb.sql
(Schema Bootstrap)
Guacamole needs a database schema before the web app can function. There are two proper ways to get the initdb.sql
file. Both methods generate the same schema content.
Method A — One-Time Command (Recommended)
This runs the official Guacamole image to output the SQL initialization script:
# Run in your project folder (make sure ./init exists)
mkdir -p ./init
# Generate initdb.sql from the official image
docker run --rm guacamole/guacamole:latest \
/opt/guacamole/bin/initdb.sh --mysql > ./init/initdb.sql
docker run --rm
runs a temporary container (no service created)./opt/guacamole/bin/initdb.sh --mysql
outputs the SQL schema for MySQL/MariaDB.
Redirect (>
) saves it to./init/initdb.sql
.
👉 Place this file in your init directory before starting the stack.
👉 If your MariaDB volume already has data, the script won’t run again—/docker-entrypoint-initdb.d/
only works on first start with an empty DB volume.
Method B — From Official Release Package
If you prefer manual download (or want to match a specific version):
- Go to the Apache Guacamole release page.
- Download the package matching your container version, e.g.:
guacamole-auth-jdbc-<VERSION
>.tar.gz - Extract it. Inside, you’ll find:
mysql/001-create
-schema.sql002-create
-admin-user.sql
... - Concatenate all SQL files in order into a single
initdb.sql
:cat
mysql/*.sql > ./init/initdb.sql
This initdb.sql
is functionally the same as Method A.
Final Notes
- Always match the Guacamole version of your
initdb.sql
with the Docker image tag (guacamole/guacamole:<tag>
). - Keep
initdb.sql
in./init/
before first DB startup. - If your DB is already initialized,
initdb.sql
won’t be applied unless you reset the volume (caution: this wipes data).
Step-by-Step Deployment on Synology
Upload Files, Create Folders, Permissions
- Create the project folder:
/volume1/docker/guacamole/
. - Inside it, create subfolders:
init/
,mariadb/
,home/
. - Put
docker-compose.yml
,.env
, andinit/initdb.sql
in place. - Ensure the shared folder owner has read/write and Docker can access it (e.g., add your user to the
docker
group or use an admin account for deployment).
Bring the Stack Up & Verify Healthchecks
Option A — DSM Container Manager (GUI):
- Open Container Manager → Projects → Create.
- Point to your project directory and import
docker-compose.yml
. - Add environment file
.env
if prompted. - Deploy. Watch the Health column until
db
is healthy andguacamole
is healthy.
Option B — SSH (CLI):
cd
/volume1/docker/guacamole
docker compose pull
docker compose up -d
docker compose ps
docker compose logs -f db
docker compose logs -f guacamole
When ready, browse to: http://<NAS_IP>:8348/
First Login and Basic Hardening
- The initial user setup depends on how you load the schema. If you used packaged SQL with an admin account, log in and change the password immediately.
- Create a non-admin user for daily use.
- Disable or delete any bootstrap accounts after you confirm your setup.
Network, Reverse Proxy, and HTTPS
Mapping Port 8348 and DSM Reverse Proxy
- The compose maps
8348:8080
. If 8348 is taken, change the left side of the mapping. - To serve via a domain, use Control Panel → Login Portal → Reverse Proxy:
- Source:
https://guac.yourdomain.com
→ Destination:http://127.0.0.1:8348
- In
guacamole
env, consider enablingREMOTE_IP_VALVE_ENABLED
and restrictingPROXY_ALLOWED_IPS_REGEX
to your proxy.
- Source:
Let’s Encrypt TLS on Synology
- In Control Panel → Security → Certificate, request/renew a Let’s Encrypt certificate for your domain.
- Bind the certificate to your reverse proxy entry so the Guacamole UI is served over HTTPS.
Performance & Stability Tips
Tuning MariaDB max_allowed_packet
& UTF-8
- Large file transfers and clipboard sync can require bigger packets. You’ve set
1GiB
, which is generous and safe for heavy sessions. utf8mb4
+utf8mb4_unicode_ci
ensure proper emoji and multilingual support.
Scheduling Backups for DB & Guacamole Home
- Back up
./mariadb
(DB) and./home
(Guacamole home). - For safer DB backups, run a dump from a temporary container:
docker exec
-it guacamole-wody-kr-mariadb \mariadb-dump -u root -p"$MYSQL_ROOT_PASSWORD" db_guacamole > /volume1/backups/db_guacamole_$(date
+%F).sql
- Keep at least one off-NAS copy.
Troubleshooting Playbook
Healthcheck Fails, Empty UI, or Login Loops
- Check logs:
docker compose logs --tail
=200 dbdocker compose logs --tail
=200 guacddocker compose logs --tail
=200 guacamole
- If
db
is not healthy: verify.env
values, folder permissions, and thatmariadb/
is empty on first run. - If the UI loads but you can’t log in: confirm schema creation. If you didn’t import schema SQL, add it under
./init/
and recreate DB volume (caution: data loss).
RDP/SSH/VNC Connection Errors
- From the Guacamole UI, confirm Hostname/IP and Port for each connection.
- Ensure target hosts allow inbound RDP/SSH/VNC from the NAS.
- If connecting across VLANs/VPNs, verify routing and firewall rules.
Security Best Practices
Secrets, Users, and Network Isolation
- Store secrets in
.env
. Never commit them. - Use strong, unique passwords for
MYSQL_ROOT_PASSWORD
andMYSQL_USER_PASSWORD
. - Keep the stack on a private bridge network. Only expose the web port you need (8348).
- Restrict DSM user accounts and use 2-step verification where possible.
- Consider Guacamole extensions for SSO/OIDC if your environment supports it.
FAQs (Quick Answers)
Q1. Can I change the external port from 8348 to something else?
Yes. Edit ports: - "8348:8080"
to your preferred port, e.g., "9000:8080"
, then redeploy.
Q2. Do I have to use MariaDB, or can I use PostgreSQL?
You can use PostgreSQL. This compose uses MariaDB for simplicity. If you switch, update environment variables and supply the correct schema.
Q3. Where do I put Guacamole extensions (like auth or branding)?
Place JAR files under ./home/extensions/
(create the folder if missing), then restart the guacamole
container.
Q4. How do I enable HTTPS?
Use Synology’s reverse proxy with a Let’s Encrypt certificate and point it to http://127.0.0.1:8348
. Access via https://your-domain
.
Q5. My DB volume already has data. Will initdb.sql
run again?
No. The docker-entrypoint-initdb.d
scripts only run on first start with an empty data directory.
Q6. How do I update to the latest Guacamole?
Run:
docker compose pull
docker compose up -d
Read release notes first and back up DB and home/
before upgrading.
Conclusion & Next Steps
You now have Running Guacamole on Synology NAS by Docker Installation from end to end—folders, compose, .env
, initdb.sql
, Synology reverse proxy, and TLS. This setup is fast to deploy, easy to maintain, and secure when paired with good password hygiene and backups. From here, add connections (RDP/SSH/VNC), explore extensions, and fine-tune your reverse proxy and firewall.
External Reference:
- Apache Guacamole official docs: https://guacamole.apache.org/