I’m using tailscale sidecars to access my containers. This gives me a unique IP for each service, as well as a https endpoint.
The benefits are:
- you have an IP per service, so you can bind to ports freely from within your container
- the containers appear on your VPN, so you can securely get to your services from outside your LAN
- you can optionally enable Tailscale Serve, which will automatically issue a LetsEncrypt cert for you and create a redirect from 443 → your http port. No more browser warnings
- You can optionally enable Tailscale Funnel, which will then stick that https service on the public internet
- Tailscale has a bunch of other (optional) fancy features, like SSH auth, exit nodes, selective sharing with other tailscale users etc
Some drawbacks:
- you can’t access your services if you’re not on the vpn (except via Funnel)
- more containers and config to manage
- the server parts are not open source
- commercial use needs a paid plan
It’s a bit complicated to set up the first one, but once that’s done you have a cookie cutter approach you can use for all your containers. I’ll try to explain how below.
I’m not on Electric Eel yet, (using jlmkr + dockge) but I believe this approach will just work under EE as well. As a general rule, dockge is better for managing compose stacks (like the one below) but portainer or plain docker are also possible.
Here’s an example compose.yaml
:
services:
wiki:
container_name: ${NAME}
image: m0wer/tiddlywiki:latest
restart: unless-stopped
volumes:
# You could mount a directory from your NAS instead
- wiki:/var/lib/tiddlywiki
environment:
- NODE_MEM=256
depends_on:
- tailscale-sidecar
# Black magic - delegate networking to the other container.
# TiddlyWiki will bind on port 8080 and just appear on the tailscale VPN.
network_mode: service:tailscale-sidecar
tailscale-sidecar:
image: tailscale/tailscale:latest
hostname: ${NAME}
restart: unless-stopped
volumes:
- tailscale-data:/var/lib/tailscale
- /dev/net/tun:/dev/net/tun
# only if you want tailscale to manage https for you
- $PWD/ts/config:/config
environment:
- TS_AUTHKEY=${TS_AUTHKEY}?ephemeral=false
- TS_STATE_DIR=/var/lib/tailscale
# By tagging this node, you can configure an ACL to allow auto-provisioning the funnel.
# Only required for tailscale-managed https.
- TS_EXTRA_ARGS=--advertise-tags=tag:container
# This config is only required if you want the HTTPS tunnel.
- TS_SERVE_CONFIG=/config/serve.json
- TS_ACCEPT_DNS=true
cap_add:
- net_admin
- sys_module
# This section is optional, it sticks a link in the dockge panel so you can click through to your service.
x-dockge:
urls:
- https://${NAME}.${DOMAIN}/
networks: {}
volumes:
tailscale-data:
driver: local
wiki:
driver: local
Variables are in .env
(which dockge understands), but you could just put them inline if you want to.
TS_AUTHKEY=ts-client-create-this-in-tailscale-admin-settings-oauth-clients
NAME=wiki
# Find this under DNS in the tailscale web console. You'll also need to enable HTTPS certs and MagicDNS on that page if you want tailscale HTTPS to work.
DOMAIN=tailnet-name.ts.net
The tailscale serve config file needs to be created outside of dockge, which is a bit of a pain. Not needed unless you want tailscale to set up a https tunnel for you. It looks like this.
There’s only two things to configure:
- the port (8080) that your service listens on. Incoming requests on your tailscale network on port 443 will be redirected there.
- AllowFunnel can be set to
true
. This will expose your service on the public internet, so make sure you have authentication set up within the app if you choose to do this (and keep up with patches, etc).
{
"TCP": {
"443": {
"HTTPS": true
}
},
"Web": {
"${TS_CERT_DOMAIN}:443": {
"Handlers": {
"/": {
"Proxy": "http://127.0.0.1:8080"
}
}
}
},
"AllowFunnel": {
"${TS_CERT_DOMAIN}:443": false
}
}
One last thing (again, only if you want https): this is a one-time change to your tailscale ACL that lets any container create a funnel:
"nodeAttrs": [
{
// there's some default config that lives here...
},
{
// Funnel policy, which lets containers enable Funnel.
"target": ["tag:container"],
"attr": ["funnel"],
},
],
I cobbled most of this together from this blog post. If you like this approach but don’t want to use tailscale or the http tunnel, the same thing can be achieved with other tools:
- About half the complexity above is just there to enable the https tunnel, you can drop those parts if you just want a dedicated IP
- The same
network_mode: service
trick can be used with other vpn containers, e.g. tinc
- If you never need to access your services outside your local network, ditch the vpn and use macvlan as others have suggested. I do this for my jails but haven’t tried it with containers