Apache Guacamole with Docker: Browser-Based RDP, VNC and SSH

This guide is for sysadmins and MSP technicians who want one browser-based door to the RDP, VNC and SSH servers on an office network without installing a client on every laptop. Apache Guacamole is the free, open-source (Apache License 2.0) gateway that does this, and the official Docker images are the quickest way to run it. The short answer: you need three containers, guacd, the Guacamole web app and a database; you must initialise the database schema yourself before the first start; and you should put the stack behind an HTTPS reverse proxy with WebSocket support before anyone outside the LAN touches it. Every command below follows the Apache Guacamole Manual for version 1.6.0, the current release as of 2 November 2026. We have not run this stack in a lab yet, so session speed and how many users one host carries are “Not tested yet”.

Guacamole with Docker at a glance

ItemDetails (verified 2 November 2026)
Current version1.6.0, released 22 June 2025; the guacamole/guacamole and guacamole/guacd images carry the same tag
LicenceFree, open source, Apache License 2.0; no paid edition or feature gate
Containersguacamole/guacd (protocol proxy), guacamole/guacamole (web app on Tomcat 9), plus PostgreSQL, MariaDB/MySQL or SQL Server
ProtocolsRDP, VNC, SSH, Telnet and Kubernetes console, rendered in any HTML5 browser
Portsguacd 4822 (keep inside the Docker network), web app 8080 (behind a proxy on 443)
Default loginguacadmin / guacadmin, created by the database schema; change it at first login
Host requirementsA Linux host with Docker Engine and Docker Compose; users need only a browser
Session speed, concurrent usersNot tested yet

What the three containers do

guacamole/guacd is the native daemon that speaks RDP, VNC, SSH, Telnet and Kubernetes, with FreeRDP plugins and terminal fonts included. guacamole/guacamole is the Java web application on Tomcat 9, configured entirely through environment variables. The database holds users, groups and connections and is the only container that must keep its data across upgrades. One warning from the manual bears repeating: guacd performs no authentication, so never publish port 4822 on the host; anyone reaching it could use guacd as a jump point into your network.

Step 1: generate the database schema

Guacamole does not create its own tables. For PostgreSQL the image ships a helper that prints the full schema:

docker run --rm guacamole/guacamole:1.6.0 /opt/guacamole/bin/initdb.sh --postgresql > initdb.sql

For MariaDB or MySQL replace --postgresql with --mysql. The file goes into the database container’s /docker-entrypoint-initdb.d/ directory, which the official postgres, mariadb and mysql images run once, on the first start with an empty data directory. Start the database before the script is in place and you get empty tables and a login that rejects every password; wipe the data volume and start again. The manual also documents a named-volume method; the file approach is easier to inspect and back up.

Step 2: write docker-compose.yml

This example uses PostgreSQL with the image names, ports and variable names from the manual. Pin the Guacamole tag so an upgrade is a deliberate change, not a surprise on docker compose pull.

services:
  guacd:
    image: guacamole/guacd:1.6.0
    restart: unless-stopped

  db:
    image: postgres:16
    restart: unless-stopped
    environment:
      POSTGRES_DB: guacamole_db
      POSTGRES_USER: guacamole_user
      POSTGRES_PASSWORD: replace-with-a-long-random-value
    volumes:
      - ./initdb.sql:/docker-entrypoint-initdb.d/initdb.sql:ro
      - pgdata:/var/lib/postgresql/data

  guacamole:
    image: guacamole/guacamole:1.6.0
    restart: unless-stopped
    depends_on:
      - guacd
      - db
    environment:
      GUACD_HOSTNAME: guacd
      POSTGRESQL_HOSTNAME: db
      POSTGRESQL_DATABASE: guacamole_db
      POSTGRESQL_USERNAME: guacamole_user
      POSTGRESQL_PASSWORD: replace-with-a-long-random-value
      REMOTE_IP_VALVE_ENABLED: "true"
    ports:
      - "127.0.0.1:8080:8080"

volumes:
  pgdata:

Three details matter. GUACD_HOSTNAME is the container name of guacd; GUACD_PORT defaults to 4822 and can be omitted. The POSTGRESQL_* values must match the POSTGRES_* values on the database service exactly, or the web app starts but no login succeeds. Binding 8080 to 127.0.0.1 keeps the unencrypted Tomcat port off the network until the proxy is in place. For MariaDB the variables are MYSQL_HOSTNAME, MYSQL_DATABASE, MYSQL_USERNAME and MYSQL_PASSWORD.

Step 3: start and log in

docker compose up -d
docker compose logs -f guacamole

Open http://127.0.0.1:8080/guacamole/ on the host, or through an SSH tunnel, and sign in as guacadmin with password guacadmin. The manual says to change it immediately; the cleanest sequence is to create your own administrator under Settings, Users, log in as it and delete guacadmin. WEBAPP_CONTEXT: ROOT serves the app at the site root instead of /guacamole/. If the page does not load, check docker logs on the Guacamole container: the usual causes are a schema that never ran and mismatched database credentials.

Step 4: publish it over HTTPS

The manual calls a reverse proxy “highly recommended” for production: Tomcat stays on 8080 as an unprivileged process while Nginx, Apache or Caddy owns 443. Guacamole needs two things from the proxy, WebSocket upgrades and no response buffering. The Nginx block from the manual:

location /guacamole/ {
    proxy_pass http://127.0.0.1:8080;
    proxy_buffering off;
    proxy_http_version 1.1;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $http_connection;
    access_log off;
}

Without proxy_buffering off the HTTP fallback tunnel stalls. REMOTE_IP_VALVE_ENABLED in the compose file makes login history record the user’s address rather than the proxy’s; if the proxy is not on a private address, set PROXY_ALLOWED_IPS_REGEX. Do not proxy over AJP: WebSocket does not work through it.

Step 5: harden the login page

  • Brute-force blocking is enabled by default in the Docker image: after 5 failed attempts the client address is denied for 300 seconds. BAN_MAX_INVALID_ATTEMPTS and BAN_ADDRESS_DURATION change the thresholds.
  • Second factor. Add TOTP_ENABLED: "true" and each user is asked to enrol an authenticator app, via QR code, after the next successful password login. Duo is the alternative.
  • Directory accounts. LDAP or Active Directory can authenticate users while the database keeps the connection list; the manual recommends exactly that pairing. SAML, OpenID Connect and CAS cover single sign-on.
  • Network position. Guacamole replaces an exposed RDP port; it should not itself be exposed carelessly. Many teams keep the login page reachable only through WireGuard or OpenVPN and use Guacamole for the clientless layer inside. Our remote access security checklist covers logging and backups.

Adding RDP, VNC and SSH connections

RDP to a Windows server

Under Settings, Connections, New Connection, choose RDP and enter the host and port 3389. Modern Windows requires Network Level Authentication: store the username, password and domain in the connection, or leave them empty and let Guacamole prompt the user. Set Security mode to nla for a Windows host or vmconnect for a Hyper-V console; tick ignore-cert only for self-signed hosts on a trusted LAN. Resize method display-update lets the session follow the browser window on RDP 8.1 and newer. File transfer is off by default; enable-drive with a drive-path gives users a virtual drive stored on the Guacamole server.

VNC to a Linux or macOS desktop

VNC connections take the host and port 5900 plus the display number. The manual names RealVNC and TigerVNC as servers that did well in the project’s own testing and notes that VNC through Guacamole tends to use less bandwidth than a direct client. Server setup is on our TigerVNC and TightVNC pages.

SSH with keys and host verification

SSH sessions are a server-side terminal emulator drawn into the browser. Besides a password, you can paste an OpenSSH-format private key into private-key, with an optional passphrase. By default Guacamole does not verify SSH host identity; paste a known_hosts line into host-key, or place an ssh_known_hosts file in GUACAMOLE_HOME, before using SSH across the internet.

Recording, upgrades and backups

Any protocol can be recorded by setting recording-path on the connection. Recordings play back in the browser from the connection history screen through the session recording player extension (default search path /var/lib/guacamole/recordings), and guacenc converts them to video. guacd writes the files, so in Docker the recording directory must be a volume visible to both the guacd and web containers, and it may contain typed passwords.

Upgrades are a tag change on the two Guacamole images followed by docker compose pull and docker compose up -d. Across versions, check the schema/upgrade/ directory of the JDBC extension for upgrade-pre-VERSION.sql scripts newer than your current version; they run in order against the database, and permissions must be re-granted afterwards. A backup is the database dump plus the compose file and any custom GUACAMOLE_HOME extensions.

Limitations

  • Guacamole only reaches machines that already run an RDP, VNC or SSH server and are routable from guacd. It is not an ID-based remote support tool; for that compare RustDesk Server and MeshCentral on our self-hosted remote access page.
  • There is no Windows build of the server; Docker on Linux or a native Linux install are the supported routes.
  • Latency, image quality under load and sessions per VPS are “Not tested yet” here.

Who it suits and who should look elsewhere

Guacamole with Docker fits a team that already has RDP, VNC and SSH hosts, wants browser access for staff, contractors or students, and can run one Linux host with a reverse proxy. It is a poor fit for ad-hoc support of home PCs behind NAT, for shops without Linux skills, or for anyone who needs a Windows-only server. There, self-hosted RustDesk or MeshCentral, or a mesh VPN such as Tailscale in front of ordinary RDP clients, is the better path.

FAQ

What is the Apache Guacamole default login?

The database schema creates an administrator named guacadmin with the password guacadmin. The manual says to change it as soon as database authentication is confirmed working.

Is there an official Apache Guacamole Docker image?

Yes: guacamole/guacamole for the web application and guacamole/guacd for the proxy daemon, updated with each release; both have a 1.6.0 tag.

Does Guacamole run on Windows?

The server does not. It ships as source, a Java web archive and Docker images for Linux. Windows users connect from a browser, and Windows servers are reached over RDP.

Why does Guacamole say the remote desktop server is currently unreachable?

Either the web app cannot reach guacd or guacd cannot reach the target. Check that GUACD_HOSTNAME matches the guacd container name and that the target’s RDP, VNC or SSH port is open from the Docker host.

How do I copy and paste in a Guacamole session?

Open the Guacamole menu with Ctrl+Alt+Shift; the clipboard box at the top shows text copied in the session and accepts text to paste remotely. Where the browser’s clipboard API is allowed, the local clipboard syncs automatically. Administrators can disable clipboard access per connection.

Last updated: 2 November 2026 · CtrlRemote editorial team. Licence, version and platform details are checked against each developer's official documentation.

Submit your application