← All articles
two green circuit boards on wooden surface

Apache Guacamole: Self-Hosted Remote Desktop Gateway Without a Client

Infrastructure 2026-03-21 · 4 min read remote-desktop guacamole ssh vnc rdp
By Selfhosted Guides Editorial TeamSelf-hosting practitioners covering open source software, home lab infrastructure, and data sovereignty.

Apache Guacamole is a clientless remote desktop gateway. You access your servers through a web browser — no plugins, no desktop clients, no Java applets. It supports SSH, VNC, RDP, and Telnet connections, all behind a single authenticated web interface.

Photo by Jeff Loucks on Unsplash

If you manage more than a couple of machines and find yourself juggling SSH terminals, RDP sessions, and VNC viewers, Guacamole consolidates all of that into one place.

Why Guacamole Over Direct Connections

The obvious question: why not just SSH directly or use a native RDP client?

A few reasons stand out in practice:

Deploying With Docker Compose

The official Guacamole setup involves three components: the web application (guacamole/guacamole), the backend daemon (guacamole/guacd), and a database. Here is a Docker Compose configuration using PostgreSQL:

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

  postgres:
    image: postgres:16
    container_name: guacamole-db
    restart: unless-stopped
    environment:
      POSTGRES_DB: guacamole_db
      POSTGRES_USER: guacamole_user
      POSTGRES_PASSWORD: ${DB_PASSWORD}
    volumes:
      - ./db-data:/var/lib/postgresql/data
      - ./init:/docker-entrypoint-initdb.d

  guacamole:
    image: guacamole/guacamole
    container_name: guacamole
    restart: unless-stopped
    depends_on:
      - guacd
      - postgres
    environment:
      GUACD_HOSTNAME: guacd
      POSTGRESQL_HOSTNAME: postgres
      POSTGRESQL_DATABASE: guacamole_db
      POSTGRESQL_USER: guacamole_user
      POSTGRESQL_PASSWORD: ${DB_PASSWORD}
    ports:
      - "8080:8080"

Before starting, you need to generate the database initialization script:

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

This creates the SQL schema that PostgreSQL will execute on first boot. Then bring everything up:

docker compose up -d

Navigate to http://your-server:8080/guacamole and log in with the default credentials guacadmin / guacadmin. Change these immediately.

Adding Your First Connection

In the Guacamole admin panel, go to Settings > Connections > New Connection. Here is what a typical SSH connection looks like:

For RDP connections to a Windows machine:

VNC works the same way — specify the host, port (usually 5900+display number), and password.

Like what you're reading? Subscribe to Self-Hosted Weekly — free weekly guides in your inbox.

Organizing Connections With Groups

Once you have more than a handful of connections, create connection groups to keep things manageable. A practical structure:

├── Proxmox Hosts
│   ├── pve-01 (SSH)
│   ├── pve-02 (SSH)
├── VMs - Linux
│   ├── docker-host (SSH)
│   ├── media-server (SSH)
├── VMs - Windows
│   ├── win-workstation (RDP)
│   ├── win-server (RDP)
├── Network Gear
│   ├── switch-01 (SSH)
│   ├── firewall (SSH)

Groups can also be configured as "balancing groups" that distribute connections across multiple backends — useful for accessing a cluster of identical machines.

User Management and Access Control

Guacamole has a granular permission system. You can create users and control exactly which connections they can see and use. This is valuable when:

Each user can be restricted to specific connections, prevented from creating new ones, and have their sessions recorded.

Putting It Behind a Reverse Proxy

Running Guacamole on port 8080 over HTTP is fine for initial setup, but for production use you want HTTPS. Here is an Nginx configuration:

server {
    listen 443 ssl;
    server_name guac.yourdomain.com;

    ssl_certificate /etc/letsencrypt/live/guac.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/guac.yourdomain.com/privkey.pem;

    location / {
        proxy_pass http://localhost:8080/guacamole/;
        proxy_buffering off;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $http_connection_upgrade;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

The WebSocket upgrade headers are critical — without them, the remote desktop stream will not work. If you use Caddy or Traefik, both handle WebSocket proxying with less configuration.

Enabling Two-Factor Authentication

Guacamole supports TOTP (time-based one-time passwords) as a second factor. Add the TOTP extension to your Docker environment:

environment:
  TOTP_ENABLED: "true"

After restarting, each user will be prompted to set up TOTP on their next login. Since Guacamole is often the single entry point to your infrastructure, enabling 2FA here is especially important.

Session Recording for Auditing

Enable session recording by adding parameters to individual connections:

Mount a volume for /recordings in your guacd container. Recorded sessions are stored as raw Guacamole protocol data and can be replayed with the guacenc tool:

docker exec guacd guacenc /recordings/session-id.guac

This produces an M4V video file of the session.

Performance Tuning

A few settings that matter for a smooth experience:

When Guacamole Might Not Be the Right Fit

Guacamole adds a layer between you and your machines. For latency-sensitive work like gaming or video editing over RDP, a native client will always perform better. If you only manage one or two Linux servers, plain SSH is simpler. And if your primary need is file transfer rather than interactive sessions, tools like SFTPGo or Filestash are more appropriate.

But for a homelab with a mix of Linux and Windows machines that you want to access from anywhere through a single web interface, Guacamole is hard to beat. It has been an Apache project since 2014, the community is active, and the Docker deployment is straightforward enough to get running in an afternoon.

Get free weekly tips in your inbox. Subscribe to Self-Hosted Weekly