How I Access My Homelab Remotely
I use Apache Guacamole to access my homelab remotely, primarily when I need SSH access to one of my servers.
The setup combines Guacamole, SSH, Caddy, Cloudflare and two-factor authentication (2FA).
The result is a convenient remote-access system without exposing SSH directly to the Internet.
The Basic Idea
My remote access path looks like this:
flowchart LR
A[My Computer] --> B[Cloudflare]
B --> C[Caddy]
C --> D[Guacamole]
D --> E[SSH]
E --> F[My Homelab Servers]
The important point is that SSH itself is not exposed to the Internet.
Only the Guacamole web interface is exposed through the reverse proxy.
Why I Use Guacamole
Apache Guacamole provides a browser-based interface for remote access.
I don't need an SSH client on the computer I am using. I simply open a web browser and connect to my Guacamole installation.
Once authenticated, Guacamole can establish SSH connections to my internal servers.
This is particularly useful when I am working from someone else's computer or when I simply want a convenient way to access my servers without setting up an SSH client.
My Remote Access Path
When I connect remotely, the traffic follows this path:
flowchart LR
A[Remote Computer] -->|HTTPS| B[Cloudflare]
B -->|HTTPS| C[Caddy Reverse Proxy]
C --> D[Guacamole]
D -->|SSH| E[Internal Server]
Each component has a specific job.
1. Cloudflare
Cloudflare provides the public-facing DNS and proxy for my domain.
My Guacamole installation is accessed through a subdomain.
The connection from my remote computer to Cloudflare is encrypted using HTTPS.
Cloudflare also provides an additional layer between the Internet and my home network.
2. Caddy
Caddy runs on my main infrastructure server and acts as my reverse proxy.
It receives the HTTPS request and forwards it to the Guacamole container running on another server.
The public-facing URL therefore does not expose the internal Docker port directly.
My Caddy configuration follows the same general pattern I use for my other services:
guacamole.example.com {
reverse_proxy INTERNAL_SERVER:8562
}
The actual domain and internal address are specific to my homelab.
3. Guacamole
Guacamole runs as a Docker container.
My current deployment uses the jwetzell/guacamole image with the TOTP authentication extension enabled.
The relevant part of my Docker Compose configuration is:
services:
guacamole:
container_name: guacamole
stop_grace_period: 60s
image: jwetzell/guacamole
restart: unless-stopped
ports:
- 8562:8080
volumes:
- ./guacamole:/config:rw
environment:
TZ: America/Santo_Domingo
EXTENSIONS: "auth-totp"
This is my actual deployment rather than a generic Guacamole configuration.
The timezone shown above is specific to my own server and should be changed if you are following this example.
SSH Is Still Internal
One of the most important aspects of this arrangement is that I do not forward SSH ports from my router to the Internet.
My servers continue to listen for SSH connections on the internal network.
Guacamole connects to those servers from inside my network.
Conceptually:
flowchart TB
A[Internet] --> B[Cloudflare]
B --> C[Caddy]
C --> D[Guacamole]
D --> E[Internal Network]
E --> F[SSH Server]
There is therefore no requirement for something like:
Internet
|
| TCP 22
v
Router
|
v
SSH Server
That is a configuration I deliberately avoid.
Two-Factor Authentication
I have also enabled TOTP two-factor authentication for Guacamole.
The login process therefore requires more than just my Guacamole username and password.
The general authentication flow is:
flowchart LR
A[Guacamole Login] --> B[Username + Password]
B --> C[TOTP Code]
C --> D[Authenticated]
D --> E[SSH Connection]
This is particularly important because Guacamole is intentionally accessible from the Internet.
Even though the actual SSH services remain internal, the Guacamole login page itself is a public-facing service and should therefore be protected properly.
Why I Prefer This Arrangement
There are several reasons I prefer this approach.
No Direct Internet-Facing SSH
My SSH services are not directly exposed to the Internet.
That means I don't need to open TCP port 22 on my router just so that I can administer my servers remotely.
Browser-Based Access
I can access my servers from a normal web browser.
I don't need to install PuTTY, Windows Terminal, an SSH client or any other software on the remote computer.
One Entry Point
Guacamole gives me a single place from which I can access my various servers.
Instead of remembering different public addresses and ports, I connect to Guacamole and select the server I want.
Works Well With My Existing Infrastructure
Guacamole fits naturally into the architecture I already use.
I already have:
- Cloudflare handling my public DNS and proxying
- Caddy handling reverse proxying
- Docker running my applications
- SSH providing administration access to my Linux servers
- ntfy providing notifications for many of my homelab services
Guacamole simply becomes the controlled entry point for remote administration.
My Overall Architecture
Putting everything together, my remote-access architecture looks like this:
flowchart TB
A[Remote Computer]
B[Cloudflare]
C[Caddy]
D[Guacamole]
E[Internal Network]
F[micropc]
G[micro2pc]
H[minipc]
A -->|HTTPS| B
B --> C
C --> D
D -->|SSH| E
E --> F
E --> G
E --> H
The Internet-facing portion ends at Guacamole.
From Guacamole onwards, the connections are made to my internal servers.
Security Considerations
Remote administration deserves more care than an ordinary web application.
My approach is based on several layers rather than relying on one security mechanism.
HTTPS
The Guacamole interface is accessed over HTTPS through Caddy and Cloudflare.
Strong Authentication
Guacamole requires a username and password.
Two-Factor Authentication
TOTP provides an additional authentication factor.
No Public SSH
I don't expose SSH directly through my router.
Reverse Proxy
Caddy provides the public-facing entry point rather than exposing the Guacamole Docker port directly to the Internet.
Cloudflare
Cloudflare provides another layer between the public Internet and my homelab.
None of these measures should be regarded as making a service automatically secure. Keeping Guacamole, its container image, the operating system and the reverse proxy up to date is still important.
What Happens When I Need to Access a Server
In practice, the process is quite simple.
- I open a browser on the computer I am using.
- I connect to my Guacamole URL.
- I authenticate with my username and password.
- I enter my TOTP code.
- Guacamole presents my available connections.
- I select the server I want to administer.
- Guacamole establishes an SSH connection to that server.
- I get an SSH terminal directly in my browser.
The whole process feels much like connecting to a normal web application.
The difference is that the web application is providing me with an SSH session to my own servers.
Advanced Example — My Actual Deployment
The following is provided as a reference rather than as a universal Docker Compose configuration.
It is based on my own homelab deployment:
services:
guacamole:
container_name: guacamole
stop_grace_period: 60s
image: jwetzell/guacamole
restart: unless-stopped
ports:
- 8562:8080
volumes:
- ./guacamole:/config:rw
environment:
TZ: America/Santo_Domingo
EXTENSIONS: "auth-totp"
The important things to notice are:
- Guacamole runs in Docker.
- The container listens on port
8080. - I publish it on host port
8562. - Configuration is stored outside the container.
- The TOTP authentication extension is enabled.
- Caddy provides the public reverse-proxy entry point.
Your own paths, ports, timezone and reverse-proxy configuration will probably be different.
Final Thoughts
For my homelab, Guacamole provides a useful compromise between security and convenience.
I can administer my servers remotely without exposing SSH directly to the Internet, while still getting the convenience of a browser-based SSH client.
The complete chain is:
flowchart LR
A[Me] --> B[Browser]
B -->|HTTPS| C[Cloudflare]
C --> D[Caddy]
D --> E[Guacamole]
E -->|SSH| F[Homelab Servers]
For me, the key principle is simple:
Expose the minimum necessary service to the Internet, protect that service with multiple layers of authentication and encryption, and keep the actual administration services such as SSH inside the trusted network.