Skip to content

Installing Apache guacamole with Docker & Traefik (avoiding pitfalls)

  • by

Tasty tasty guacamole

tl;dr: Some parts of another guide no longer work, the biggest pitfall is execution permission on the directory of the database init script


A million years ago in 2018 I installed guacamole for reasons that I forget.

As the link says, guacamole is clientless remote desktop software from Apache, so you can remote in without needing a client. Nifty. I wanted this installed here in the distant present as I’d been at my mum’s frequently for reasons of boiler replacement, gas network fixing pressure, and such. My laptop works fine but there were a couple of times I missed having the context of my home setup. I might have gotten away with ssh access as usual but wanted the option of GUI. So I returned to guacamole.

Installing guacamole with docker-compose and traefik

Since I couldn’t find the details of how I installed guacamole in the annals of history, I started anew. There is a guide, enticingly titled: “Install Guacamole on Docker with Traefik and 2FA“. I don’t particularly need the 2FA aspect, but I do use traefik! Please check this page for the issue with that guide; it was a useful starting point for me, but I had to track down some issues.

Apache themselves do a ‘guacamole with docker’ setup guide but make no mention of docker-compose. It is helpful in some aspects though.


version: '3.4'
    image: guacamole/guacd
    container_name: guacd
    hostname: guacd
    restart: unless-stopped
      - ./guacd/drive:/drive:rw
      - ./guacd/record:/record:rw
      - guacamole_network

    image: guacamole/guacamole
    container_name: guacamole
    hostname: guacamole
    restart: unless-stopped
      - guacd
      - guacamole-db
      GUACD_HOSTNAME: guacd
      POSTGRES_HOSTNAME: guacamole-db
      POSTGRES_DATABASE: guacamole_db
      POSTGRES_USER: guacamole_user
      #MYSQL_HOSTNAME: guacamole-db
      #MYSQL_DATABASE: guacamole_db
      #MYSQL_USER: guacamole_user
      #TOTP_ENABLED: 'true'
      - guacd
      - 'traefik.enable=true'
      - 'traefik.http.routers.guacamole.rule=Host(`guacamole.${DOMAIN}`)'
      - "traefik.http.routers.guacamole.service=guacamole"
      #- 'traefik.http.routers.guacamole.entrypoints=web-secure'
      - 'traefik.http.routers.guacamole.tls=true'
      - "traefik.http.routers.guacamole.tls.certresolver=myresolver"
      - "traefik.http.routers.guacamole.middlewares=guac-auth,guacprefix"
      - "traefik.http.middlewares.guac-auth.basicauth.users=USERNAME:PASSWORD"
      - "traefik.http.middlewares.guacprefix.addprefix.prefix=/guacamole"
      - ""
      #- "[0].main=*.${DOMAIN}"
      #- "traefik.http.routers.guacamole.tls.options=myTLSOptions@file"
      #- "traefik.http.routers.guacamole.middlewares=guacamoleMdl"
      - guacamole_network
      - traefik_default

    #image: mysql/mysql-server
    image: postgres:13.4-buster
    container_name: guacamole-db
    hostname: guacamole-db
      POSTGRES_USER: guacamole_user
      POSTGRES_DB: guacamole_db
      PGDATA: /var/lib/postgresql/data/guacamole
    restart: unless-stopped
      #- ./init/initdb.sql:/initdb.sql  #DB configuration file
      - ./init/:/docker-entrypoint-initdb.d  # make sure dir is `chmod +x` 
      - ./pgdata:/var/lib/postgresql/data:Z
      #- ./guac-data/database:/var/lib/mysql/:rw
      - guacamole_network

    external: true
    external: false

Or download it here: docker-compose.yaml

I’ve left the changes I made from the linked guide in. You should do a couple of things before using this:

  • create a .env file in the same directory with GUACAMOLE_PASSWORD and DOMAIN variables set (and set up DNS for guacamole.whateverthatdomainis.tld)
  • change or remove the basicauth middleware to set a username and password; guacamole itself has password based auth and can be set up for 2FA so this isn’t terribly necessary, but I keep non-public things away from prying eyes

    if you need to generate the password hash, I have a guide on how to generate the hash without htpasswd
  • change the network to match whatever you are using for traefik (and any other naming convention alignments)
  • create directories for guacd, guacamole itself and the database data

Generate the database initialisation file

This differs from the linked guide! You will see in the docker-compose.yaml above that the database initialisation file is stored in a directory- this is because the enclosing directory needs the execution bit set (see eg in this other docker-compose.yaml), or it will be ignored. So run:

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

If you prefer MySQL / MariaDB, substitute --mysql in that command.

Start it up and log in

The guacamole instance should be accessible on https://guacamole.yourdomain.tld with the default login being guacadmin / guacadmin like the other guide, unless you changed this. Regardless, you should do as they suggest:

I suggest you duplicate the default account and create a new administrator account. Then login with the new account and delete the default one.

Errors and Pitfalls

I spent some time chasing down errors, so in case you find yourself down a similar rabbit hole, here’s what they might be and why.

404 Not Found

Following the guide and filling in the relevant details (really the password and the appropriate traefik labels), you’ll probably see something like this:

404 page given by guacamole when pointed at the wrong URL

For whatever reason, the addprefix middleware in that guide didn’t work for me off the bat.

Error: An error has occurred and this action cannot be completed

When you fix that or add /guacamole/ to the URL, you’ll probably see:

An error has occurred and this action cannot be completed. If the problem persists, please notify your system administrator or check your system logs.

Ah, hmm. Let’s do as it suggests and check the logs:

guacamole       | 11:13:46.067 [http-nio-8080-exec-4] ERROR - Unexpected internal error: 
guacamole       | ### Error querying database.  Cause: org.postgresql.util.PSQLException: The connection attempt failed.
guacamole       | ### The error may exist in org/apache/guacamole/auth/jdbc/user/UserMapper.xml
guacamole       | ### The error may involve org.apache.guacamole.auth.jdbc.user.UserMapper.selectOne
guacamole       | ### The error occurred while executing a query
guacamole       | ### Cause: org.postgresql.util.PSQLException: The connection attempt failed.

The connection attempt failed? But the log reports:

guacamole-db    | .2022-12-09 10:58:15.134 UTC [48] LOG:  listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432"
guacamole-db    | 2022-12-09 10:58:15.227 UTC [49] LOG:  database system was shut down at 2022-12-09 10:58:03 UTC
guacamole-db    | 2022-12-09 10:58:15.253 UTC [48] LOG:  database system is ready to accept connections
guacamole-db    |  done
guacamole-db    | server started


guacamole-db | /usr/local/bin/ ignoring /docker-entrypoint-initdb.d/*

Looking further up you’ll see:

guacamole-db    | CREATE DATABASE                                                                                                      
guacamole-db    |                                                                                                                      
guacamole-db    |                                                  
guacamole-db    | /usr/local/bin/ ignoring /docker-entrypoint-initdb.d/* 
guacamole-db    | 
guacamole-db    | waiting for server to shut down....2022-12-09 10:58:23.005 UTC [48] LOG:  received fast shutdown request
guacamole-db    | 2022-12-09 10:58:23.056 UTC [48] LOG:  aborting any active transactions
guacamole-db    | 2022-12-09 10:58:23.393 UTC [48] LOG:  background worker "logical replication launcher" (PID 55) exited with exit code 1
guacamole-db    | 2022-12-09 10:58:23.407 UTC [50] LOG:  shutting down

If you don’t have execution permissions set on the directory containing the database init script, it gets skipped!

Table ‘guacamole_db.guacamole_user’ doesn’t exist

If like me you got to this point and were getting a "table 'guacamole_db.guacamole_user' doesn't exist” error, it might be related to the log line:

guacamole-db | PostgreSQL Database directory appears to contain a database; Skipping initialization

If you are fixing the permissions issue on the database initialisation script, make sure to remove the existing database before you start up again! ie

  1. docker-compose down
  2. rm ./pgdata/
  3. docker-compose up -d

Image credit

The featured image on this article is ‘guacamole’ by stu_spivack on Flickr

Tell us what's on your mind