VPN Inside a Container
Section titled “VPN Inside a Container”You can run a real VPN inside your container and keep every published URL alive. One condition, and it decides everything:
Your provider’s WireGuard config file: your URLs keep working. Your provider’s desktop app: your URLs go dark.
Same provider. Same relay. Same encryption. Completely different outcome for everything you have published. Read the next section before you touch either one.
Why a Full Tunnel Fights Your Inbound URLs
Section titled “Why a Full Tunnel Fights Your Inbound URLs”Your container’s URLs — *-terminal-1.*, *-console-1.*, every service you publish — are reached through the Hoody Reverse Proxy, which preserves the visitor’s real IP all the way into the container. That is deliberate. It is what lets your app see who is calling, and it is what makes hooks, permissions and audit mean anything.
Now look at what that implies. Your container’s reply is addressed to a public IP. A full-tunnel VPN (AllowedIPs = 0.0.0.0/0) sends everything public into the tunnel — including that reply. The answer leaves through your VPN provider and never reaches the visitor who asked.
The symptom is not an error page. The connection is accepted, TLS completes in about 5 ms, and then it hangs for roughly two minutes before failing. We captured the container answering correctly and the answer walking straight into the tunnel:
19:30:17.461 IP <container>.76 > <visitor>.39066: Flags [S.] (on the VPN interface)Zero matching packets on the normal network interface over the same window. The container was never broken. Its reply was just leaving by the wrong door.
Hoody now holds that door open. Replies to inbound proxy traffic stay on the path they arrived on, so a full tunnel and your published URLs can coexist. What it cannot do is argue with a firewall that drops the request before any routing decision exists — which is exactly what a VPN app’s kill switch is.
Use the Config File, Not the App
Section titled “Use the Config File, Not the App”Download the WireGuard configuration from your provider and run it with wg-quick. Nothing else.
# WireGuard userspace toolsapt-get update && apt-get install -y wireguard-tools
# Put the config your provider gave you in place.# wg-quick names the interface after the filename.install -m 600 mullvad-sg-sin-001.conf /etc/wireguard/
# Upwg-quick up mullvad-sg-sin-001
# Downwg-quick down mullvad-sg-sin-001Measured on a live container with a real Mullvad WireGuard device on relay sg-sin-wg-001:
- Published URL returns HTTP 200 in about 33 ms with the tunnel up.
- It survives the tunnel being restarted — verified across three full down/up cycles.
- Egress is genuinely protected. Container exit IP
138.199.60.6, andhttps://am.i.mullvad.net/connectedreports “You are connected to Mullvad (server sg-sin-wg-001)”. The server’s own IP is not exposed.
You get the privacy you came for, and you keep every URL you published.
A VPN app with a kill switch, lockdown mode, or “block connections without VPN” applies a default-deny inbound firewall while connected. That drops the incoming connection before any routing decision happens.
No routing fix can help. There is nothing left to route.
Measured with the Mullvad desktop app on the same container:
- Kill switch armed: published URL returns HTTP 000. Nothing answers.
- Kill switch removed: HTTP 200, immediately.
- “Allow local network sharing” enabled: still HTTP 000.
That last one surprises people, so be clear about why: the edge arrives carrying the visitor’s public IP. It is not local-network traffic, and a local-network exemption will never match it.
Turning the kill switch off to get your URLs back leaves you running a VPN app with its safety feature disabled. Run the config file instead.
Verify Both Directions
Section titled “Verify Both Directions”Check the tunnel and the URLs separately. They can fail independently, and knowing which one broke saves you an hour.
# Egress: are you actually on the relay?curl https://am.i.mullvad.net/connected# You are connected to Mullvad (server sg-sin-wg-001)# Ingress: does the published URL still answer?curl -o /dev/null -s -w '%{http_code}\n' \ https://{project}-{container}-terminal-1.{server}.containers.hoody.com/# 200000 means the request never got an answer — a kill switch is armed inside the container. Go in over SSH and take the VPN down.
What Stops Working While a Full Tunnel Is Up
Section titled “What Stops Working While a Full Tunnel Is Up”These are container-outbound destinations, so keeping your inbound replies on course does nothing for them. While any full tunnel is up:
| Destination | Address | Status with a full tunnel |
|---|---|---|
| Platform DNS resolver | 169.254.169.53 | Direct queries fail |
| AI proxy | 198.51.100.1 | Unreachable |
| APT / package proxy | 198.51.100.2 | Unreachable |
Do not read more into that than it says. General internet access works. Ordinary name resolution works, because it goes through the tunnel like everything else. What you lose is the platform’s own internal shortcuts, not your connectivity.
Locked Out? You Are Not.
Section titled “Locked Out? You Are Not.”If your URLs have already gone dark, you have two ways back and both of them work:
Go in over SSH and stop the VPN.
ssh -i ~/.ssh/hoody-container-1 root@ssh.$serverName.containers.hoody.com
# then, inside the containerwg-quick down mullvad-sg-sin-001# or quit the VPN appOr restart the container. VPN interfaces and their routing do not survive a restart — that is measured, not assumed. A restart returns the container to a clean network and your URLs come back with it.
How It Works
Section titled “How It Works”There is nothing for you to configure, and that is the point.
When your container starts, Hoody installs a reply-path rule inside the container. Traffic your container initiates goes wherever your VPN sends it. Traffic that answers the Hoody edge goes back to the edge. Two directions, two paths, no configuration file of yours involved.
Two consequences worth carrying around:
- The rule is installed at container start, before anything of yours runs. It has to be in place before a VPN starts, which is why it is done there and not later.
- A tunnel that is already running when the rule is installed wins over it. This is another reason a restart fixes things: the restart puts the rule back in front.
This Is Not Network Configuration
Section titled “This Is Not Network Configuration”Two different layers, two different pages, easy to confuse:
| Network Configuration | VPN inside the container (this page) | |
|---|---|---|
| Where it runs | On the host, outside your container | Inside your container, by you |
| What it speaks | SOCKS5, HTTP, HTTPS proxies | Whatever VPN client you install |
| Setup inside the container | None | Yours to install and run |
| Can the container bypass it | No | It is the container’s own tunnel |
Use Network Configuration when you want an exit IP change with zero in-container setup and no way for the workload to escape it. Run a VPN inside the container when you need the provider’s actual tunnel — a specific relay, a WireGuard device, a client only that provider ships.
Useful Questions
Section titled “Useful Questions”Can a VPN inside my container cut off my SSH access?
Section titled “Can a VPN inside my container cut off my SSH access?”No. SSH to a Hoody container does not travel over the container’s network at all — the host brokers it through the container runtime. No route and no in-container firewall can reach it. We drove every step of our testing this way with the tunnel up and the kill switch armed.
Will my published URLs survive a full tunnel?
Section titled “Will my published URLs survive a full tunnel?”Yes, with a WireGuard config file run by wg-quick. Measured: HTTP 200 in about 33 ms with the tunnel up, across three tunnel restarts.
Why does my provider’s app break them, then?
Section titled “Why does my provider’s app break them, then?”Because its kill switch is a default-deny inbound firewall. It drops the connection before there is any routing decision left to fix. Measured: HTTP 000 armed, HTTP 200 the moment it is removed.
Does “Allow local network sharing” fix the app?
Section titled “Does “Allow local network sharing” fix the app?”No. Measured HTTP 000 with it enabled. The Hoody edge arrives with the visitor’s real public IP, so a local-network exemption never matches it.
Does the tunnel survive a container restart?
Section titled “Does the tunnel survive a container restart?”No. VPN interfaces and their routing are gone after a restart. That is a limitation when you want persistence and a feature when you want out of trouble.
Which VPN providers work?
Section titled “Which VPN providers work?”Any provider that publishes a downloadable WireGuard configuration. Mullvad is the one we measured end to end, on relay sg-sin-wg-001.
Do I still get the platform DNS resolver, AI proxy and package proxy?
Section titled “Do I still get the platform DNS resolver, AI proxy and package proxy?”No, not while a full tunnel is up — those are container-outbound and go into the tunnel. Ordinary DNS and general internet access are unaffected.
What’s Next
Section titled “What’s Next”Complete your networking setup:
- SSH Access → - Set this up first. It is the door a VPN cannot close.
- Network Configuration → - Host-level exit IP routing, zero in-container setup
- Firewall → - Host-enforced packet rules your container cannot touch
Understanding gained:
- The Hoody edge preserves the visitor’s real IP, which is why a full tunnel used to swallow your replies
- Replies to inbound proxy traffic now stay on the path they arrived on
- A WireGuard config file keeps your URLs; a VPN app’s kill switch kills them
- SSH always works, no matter what the tunnel does
- A container restart clears any VPN state
Run the tunnel, not the app. Add SSH before you add the VPN.
Privacy on the way out. Your URLs still answering on the way in.