Running Guacamole on Synology NAS by Docker Installation

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 port 8080).
  • 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/ contains initdb.sql executed once on first DB start.
home/ becomes GUACAMOLE_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.


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):

  1. Go to the Apache Guacamole release page.
  2. Download the package matching your container version, e.g.:
    guacamole-auth-jdbc-<VERSION>.tar.gz
  3. Extract it. Inside, you’ll find:

    mysql/
    001-create-schema.sql
    002-create-admin-user.sql
    ...
  4. 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

  1. Create the project folder: /volume1/docker/guacamole/.
  2. Inside it, create subfolders: init/mariadb/home/.
  3. Put docker-compose.yml.env, and init/initdb.sql in place.
  4. 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 and guacamole 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 enabling REMOTE_IP_VALVE_ENABLED and restricting PROXY_ALLOWED_IPS_REGEX to your proxy.

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 db
docker compose logs --tail=200 guacd
docker compose logs --tail=200 guacamole
  • If db is not healthy: verify .env values, folder permissions, and that mariadb/ 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 and MYSQL_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, .envinitdb.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: