Introduction
This started as an effort to use Guacamole to provide browser-based access to a VM located in a different VLAN with RDP and to use an existing haproxy reverse proxy server instead of using Guacamole’s nginx reverse proxy server.
PC (VLAN 1) -> haproxy eth0 (VLAN 1) -> haproxy eth1 (VLAN 50) -> Guacamole eth0 (VLAN 50) -> Guacamole eth1 (VLAN 10) -> VM accessed with RDP (VLAN 10)
What I expected would be an hour of work or less, ended up taking several hours as I repeatedly encountered obstacles.
While Apache provides a compiled version of guacamole-client, they do not offer a compile version of guacamole-server and both are necessary for this to work. https://guacamole.apache.org/releases/1.6.0/ Obstacle number one.
No problem, there is documentation on installing guacamole-client and guacamole-server through Docker. https://guacamole.apache.org/doc/gug/guacamole-docker.html. I chose to use Docker Compose with Portainer to deploy the stack. You can review the Docker Compose file at https://github.com/4D5A/docker/blob/main/compose-configurations/apache-guacamole/docker-compose.yml.
There is an “Important” note about setting up Postgres or MySQL/mariadb in https://guacamole.apache.org/doc/gug/guacamole-docker.html
If using PostgreSQL or MySQL for authentication, you will need to initialize the database manually. Guacamole will not automatically create its own tables, but SQL scripts are provided to do this.
The importance of this information cannot be understated. It is easy to read past and one does at one’s own peril (or at least at the risk of wasting hours of time). As the note states, when you deploy the stack, Guacamole will create its database as shown in the Docker Compose file, but “will not automatically create its own tables.” Because the tables are not created, the database schema is also missing. Thanks to this Deploy Apache Guacamole with Docker Compose article, I found the information I needed to initialize the database. Apache also provides instructions for database intialization at the following URIs.
| Database | URI |
| MariaDB or MySQL | https://guacamole.apache.org/doc/gug/mysql-auth.html |
| PostgreSQL | https://guacamole.apache.org/doc/gug/postgresql-auth.html |
| SQL Server | https://guacamole.apache.org/doc/gug/sqlserver-auth.html |
The purpose of this command is to create a directory for docker to extract the database schema from the guacamole Docker image, save it in /opt/guacamole/init, and use the .sql file to create the database schema in the Postgres or MySQL/mariadb database.
sudo mkdir -p /opt/guacamole/init
cd /opt/guacamole
sudo docker run --rm guacamole/guacamole /opt/guacamole/bin/initdb.sh --mysql | sudo tee init/01-schema.sql > /dev/null
Whether you need to include “sudo” in the above docker command depends on your Docker server configuration, if you are using root, a non-root user with sudo privileges, rootless, etc.
Another important piece of information is the initialization must be done before you deploy the stack. If you deploy the stack first, you will need to delete the stack, initialize the database, and re-create the stack. Alternatively, you can stop the Postgres or MySQL/mariadb container in the stack, delete the database’s docker volume, initialize the database, and re-deploy the stack.
The next obstacle was that Guacamole does not include any users so you will not be able to login. At this point, I just wanted to be able to login, so I logged into MySQL and created the user.
mysql -u guacadmin -p guacamole_db
INSERT INTO guacamole_entity (name, type) VALUES ('admin', 'USER');
INSERT INTO guacamole_user (entity_id, password_hash, password_salt, password_date)
VALUES (
LAST_INSERT_ID(),
UNHEX(SHA2('guacadmin', 256)),
NULL,
NOW()
);
This allowed me to login, but because my stack was running on a Docker server in VLAN 50 and outbound traffic is translated from the stack’s network to the Docker server’s ethernet adapter, there wasn’t a good method of accessing the stack through my existing haproxy. Even if I solved that issue, I would need to make it possible for Guacamole to connect to the VM in VLAN 10.
After thinking through the issue, I determined the best answer would be to run Guacamole (guacamole-client, guacamole-server, and mariadb) on a Proxmox LXC. That way I could tag the LXC’s eth0 ethernet adapter as VLAN 50, access it through the existing haproxy reverse proxy, and add a second ethernet adapter (eth1) that would be configured to connect to VLAN 10.
Building guacamole from source
This document captures a full end-to-end installation of Apache Guacamole inside a Proxmox LXC container. The goal was to deploy a self-hosted remote access gateway supporting RDP (and later extensible to SSH/VNC), backed by MariaDB and running on Tomcat 10.
The process required resolving several compatibility issues across:
- Java (JDK + build tools)
- Maven build system
- Tomcat 10 Jakarta EE migration
- XRDP session stability on the target VM
By the end, Guacamole was fully operational and able to connect reliably to remote desktop sessions.
1. Proxmox LXC Container Setup
An Ubuntu 24.04-2 (Noble Numbat) template was used as the base container.
Container configuration:
- Hostname:
guacamole - CPU: 1 core
- RAM: 1024MB
2. Installing System Dependencies
apt update && apt upgrade -y
apt install git make software-properties-common \
libcairo2-dev libjpeg-turbo8-dev libpng-dev libtool-bin \
uuid-dev libavcodec-dev libavformat-dev libavutil-dev libswscale-dev \
freerdp2-dev libpango1.0-dev libssh2-1-dev libpulse-dev \
libssl-dev libvorbis-dev libwebp-dev autotools-dev \
maven tomcat10 openjdk-11-jdk \
mariadb-server mariadb-client libmariadb-java -y
Create user:
adduser user
usermod -aG sudo user
3. Building guacd
git clone https://github.com/apache/guacamole-server.git
cd guacamole-server
autoreconf -fi
./configure --with-systemd-dir=/usr/local/lib/systemd/system
make
make install
ldconfig
4. Tomcat 10 Deployment Issue
wget -O guacamole.war \
"https://apache.org/dyn/closer.lua/guacamole/1.6.0/binary/guacamole-1.6.0.war?action=download"
cp guacamole.war /var/lib/tomcat10/webapps/
Problem
Tomcat 10 requires Jakarta EE (jakarta.), but Guacamole 1.6.0 uses javax..
5. Java Setup
update-alternatives --config java
update-alternatives --config javac
export JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64
export PATH=$JAVA_HOME/bin:$PATH
6. Building guacamole-client
wget https://apache.org/dyn/closer.lua/guacamole/1.6.0/source/guacamole-client-1.6.0.tar.gz
tar -xzf guacamole-client-1.6.0.tar.gz
cd guacamole-client-1.6.0
Build fix (fast workaround)
mvn clean install -Dmaven.javadoc.skip=true -DskipTests
mvn package -Dmaven.javadoc.skip=true -DskipTests -pl '!guacamole-common-js'
7. Jakarta Migration (Tomcat 10 Fix)
wget https://archive.apache.org/dist/tomcat/jakartaee-migration/v1.0.8/binaries/jakartaee-migration-1.0.8-bin.tar.gz
tar -xvzf jakartaee-migration-1.0.8-bin.tar.gz
cd jakartaee-migration-1.0.8
java -jar lib/jakartaee-migration-1.0.8.jar \
~/guacamole-client-1.6.0/guacamole/target/guacamole-1.6.0.war \
~/guacamole-client-1.6.0/guacamole/target/guacamole-jakarta.war
sudo cp ~/guacamole-client-1.6.0/guacamole/target/guacamole-jakarta.war \
/var/lib/tomcat10/webapps/guacamole.war
8. MariaDB Setup
Configure the database
sudo mysql_secure_installation
sudo mysql -u root -p
CREATE DATABASE guacamole_db;
CREATE USER 'guacadmin'@'localhost' IDENTIFIED BY 'guacadmin';
GRANT ALL PRIVILEGES ON guacamole_db.* TO 'guacadmin'@'localhost';
FLUSH PRIVILEGES;
EXIT;
Import Schema
cat ~/guacamole-client-1.6.0/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/schema/*.sql \
| mysql -u guacadmin -p guacamole_db
Create a guacamole user
mysql -u guacadmin -pYOURSECUREPASSWORD guacamole_db
The example mysql commands below adds a gualamole user with the credentials “guacadmin/guacadmin”.
INSERT INTO guacamole_entity (name, type) VALUES ('admin', 'USER');
INSERT INTO guacamole_user (entity_id, password_hash, password_salt, password_date)
VALUES (
LAST_INSERT_ID(),
UNHEX(SHA2('guacadmin', 256)),
NULL,
NOW()
);
9. Authentication Extension
cp guacamole-auth-jdbc-mysql-1.6.0.jar /etc/guacamole/extensions/
Drivers:
cp mysql-connector-j-8.4.0.jar /etc/guacamole/lib/
cp /usr/share/java/mariadb-java-client.jar /etc/guacamole/lib/
10. Configuration
Change mysql-password from “YOURSECUREPASSWORD” to a secure password that you create.
guacd-hostname: localhost
guacd-port: 4822
mysql-hostname: localhost
mysql-port: 3306
mysql-database: guacamole_db
mysql-username: guacadmin
mysql-password: YOURSECUREPASSWORD
mysql-ssl-mode: disabled
11. Services
systemctl restart guacd
systemctl restart tomcat10
Bind guacd:
sudo nano /usr/local/lib/systemd/system/guacd.service
[Service]
User=user
Group=user
ExecStart=
ExecStart=/usr/local/sbin/guacd -b 127.0.0.1 -f
12. RDP Issue
Symptoms:
Brief connection Immediate disconnect
Logs:
Internal RDP client disconnected
Connection closed
13. XRDP Fix
Edit the script xrdp uses to initiate the desktop environment.
nano /etc/xrdp/startwm.sh
Below is what worked for me.
#!/bin/sh
if [ -r /etc/profile ]; then
. /etc/profile
fi
startxfce4
Restart the xrdp and xrdp session manager services.
sudo systemctl restart xrdp xrdp-sesman
14. Verify that everything is working
Login to Guacamole and connect to a VM.