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
8348if 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.sqlexecuted once on first DB start.home/becomesGUACAMOLE_HOMEwhere 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 --rmruns a temporary container (no service created)./opt/guacamole/bin/initdb.sh --mysqloutputs 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:catmysql/*.sql > ./init/initdb.sql
This initdb.sql is functionally the same as Method A.
Final Notes
- Always match the Guacamole version of your
initdb.sqlwith the Docker image tag (guacamole/guacamole:<tag>). - Keep
initdb.sqlin./init/before first DB startup. - If your DB is already initialized,
initdb.sqlwon’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.sqlin place. - Ensure the shared folder owner has read/write and Docker can access it (e.g., add your user to the
dockergroup 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
.envif prompted. - Deploy. Watch the Health column until
dbis healthy andguacamoleis 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
guacamoleenv, consider enablingREMOTE_IP_VALVE_ENABLEDand restrictingPROXY_ALLOWED_IPS_REGEXto 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_ciensure 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
dbis not healthy: verify.envvalues, 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_PASSWORDandMYSQL_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/