I’ve been researching different ways to expose Docker containers to the internet. I have three services I want to expose: Jellyfin, Omnivore (Read-it-later app), and Overseerr.
I’ve come across lots of suggestions, like using Nginx with Cloudflared, but some people mention that streaming media goes against Cloudflared tunnel TOS, and instead recommend Tailscale, or Traefik, or setting up a WireGuard VPN, or using Nginx with a WireGuard VPN.
The amount of conflicting advice has left me confused. So, what would be the best approach to securely expose these containers?
Do you actually want to expose the things to “the internet”, or do you just want yourself (and an approved set of other users) to be able to access them from outside of your network?
If it’s the former, you’re going to want to learn about DNS, NAT, exposing ports, firewall settings, and network monitoring.
But if it’s the latter, then I recommend checking out tailscale because that gives you and some friends LAN-like access with a great internal DNS and it works really well.
- Never host anything that is externally accessible
- If you have to, put it behind a VPN (OVPN, Wireguard, IPSec, Tailscale, etc.)
- Certificate based authentication is preferred for VPN tunnels
- Always TLS encrypt your actual endpoints. Private CAs are most secure but a pain in the ass. Let’s Encrypt is very simple to set up in most cases.
Just my 2 cents.
How is a private CA more secure then an offline CA with cross signed intermediate signing subCA?
You’ll have to explain that one to me.
Sorry third post. Trying to summarize.
-
Get external access. Either via port-forward (you lucky American) or via VPS+ssh-tunnel or VPS+wireguard. Stay away from an hard dependency like tailscale and cloudflare (my personal opinion).
-
Setup a reverse proxy with SSL certs via let’s Encrypt (don’t go wildcard, no need to, just add complexity)
That’s the concept, implementation requires clearly extra steps…
See my wiki (https://wiki.gardiol.org/). O describe both the simple and the complex solution. But to be honest, the complex solution is not fully described yet.
Is the reason you advocate avoiding VPN dependencies simply due to downtime if the tailscale service fails? Or is there a particular security vulnerability associated with using VPN subnets?
Mostly not being dependent from a specific vendor, that’s all.
I prefer to use a VPS of my choice that I can replace when I want or need to.
As far as its backed by wireguard its safe enough I guess.
I am leaning towards Wireguard, as I don’t think I’m behind a CGNAT. But, I’ll check out your wiki for more details though. Thankyou
@shimitar’s advice is what I’d go with.
Ideally:
- Set up a Wireguard subnet. Test it thoroughly, including restarting the server a couple of times.
- Close all ports except your Wireguard ports in your server firewall. Do this manually first (not persistent) and test.
- Make the firewall changes permanent.
Then, it kinda doesn’t matter what else you do on the server, although you can fuss around with locking things down more.
Caveats:
- you won’t be able to use LetsEncrypt with this
- accessing your services from an Android phone will be futzy, because Android is too stupid to be able to use more than one VPN at a time. Unless you don’t use a VPN on your phone, in which case it won’t be an issue.
- you’ll only be able to access your server from computers/systems in your Wireguard subnet, so make sure you include multiple devices in the config from which you can ssh
Wireguard is super easy to build VPN networks with, and there are tools (e.g. dsnet) to make it even easier.
-
Slap a good reverse proxy in front of it (nginx I what I use) and set it up with HTTPS using let’s Encrypt. For added layer of security setup also some SSO like Authelia.
Or just go the VPN way but then, that will not be access from internet, only via VPN, only you will be accessing it.
Maybe something like this https://github.com/fosrl/pangolin
If you need also external access, because you are behind a CGNAT or in general have no public IP at all, get a VPS and setup some tunneling.
I don’t like tailscale/cloudflare dependency so I have a different solution.
I have documented it all here: https://wiki.gardiol.org/
Is it just you that needs access? VPN like Tailscale or Wireguard is the most secure option then, as it’s not exposing any services to the internet.
Otherwise a reverse proxy in front of things like Traefik or Nginx, make sure things are automatically updated ASAP, and make sure auth is enabled on the services.
Thankyou. Since it’s just my devices (laptop/phone) that need access, I think WireGuard—or possibly Tailscale—seems like the best solution for me.
Tailscale is very easy and handles everything for you but you are relying on their services, so if you want to be 100% self hosted Wireguard is the way to go.
Yeah, I feel like exposing ports 80 and 443 towards an up to date nginx/whatever is referred to as a super dangerous thing in this community and also the selfhosted subreddit. Recommending cloudflare is almost the default, which I find a bit sad given many people selfhost to escape the reliance on big monopolist companies.
One can add different layers of security of course, but having nginx with monitoring in it’s own VM without keys to jump to another VM is enough of risk mitigation for me.
It depends on if you want to access it from anywhere (or give others access), or if you’re only accessing your server from specific devices.
Since I only ever access my server from my phone or my desktop, I use Wireguard via wg-easy. You set it up as a docker container on your server and it gives you a neat web UI (defaults to port 51821) from which to add Wireguard clients. Once connected through Wireguard, you can access your services as if you’re on the server’s local network.
Note, you’ll of course have to open up a port for Wireguard on your router for this to work, the default being 51820.
wg-easy is fabulous! I use it too and I love it
“Secure” and “exposed” are antonyms in this scenario, that’s the nature of the beast. I use Nginx which I have a domain pointing to. Worst case scenario, a hacker brute forces access to my container and mucks around within the confines. As I understand from a WireGuard VPN, there’s an added level of security. You have to use the VPN to get access to your home ports, and then you can access your Docker containers as configured. There’s an added layer of security.
Some things to consider:
- Do you have a target on your back?
- Does your container contain sensitive data?
- If so, does your container have access to external directories?
- Does your project have security options like Geo Blocking, rate limiting, etc?
I’ve been running some local servers for a few years only behind Nginx. So far nothing bad has happened. But that doesn’t mean something bad couldn’t happen later.
Thanks for the info.
Do you have a target on your back?
No.
Does your container contain sensitive data?
No.
If so, does your container have access to external directories?
I have a hard drive mounted to the media folder that Jellyfin can access, and also a config folder. Omnivore/Overseer will probably be similar once I add them. Could this be a problem?
- '/home/${USER}/server/configs/jellyfin:/config' - '/home/${USER}/server/media:/data/media'
Does your project have security options like Geo Blocking, rate limiting, etc?
That is a good idea. Thankyou
What I was referring to is called a Bind Mount, where host directories are exposed to the docker container. You may be fine if it’s an external hard drive. I use bind mounts because they’re easier to back up, but I acknowledge they are less safe.
You may be perfectly fine as you are now. My (and others) suggestions are for added security. As it stands, if there’s no target on your bind, the only bad traffic you’ll get are from bots trying to pick away at your domain and sub domains. Generally they’re not a problem. But being extra safe costs nothing but time.
My preference is just Cloudflare with or without nginx. Not sure if you’re using a hypervisor or not but it makes things exceedingly easy and I feel plenty safe enough inside of a Cloudflare tunnel. I stream a lot of data from Jellyfin. All day long, several streams to several people for over a year now with no problems. Last I knew, Cloudflare removed the language about video streaming from their TOS. Not sure if that’s changed but functionality on my end hasn’t.
I am using Unraid but I’ve installed the Cloudflare tunnel in docker containers and TrueNAS without many issues. Takes a bit of copying/pasting to get set up but it’s not terrible and everything is very responsive to make sure you’re doing things correctly.
+1 for cloudflare. But I don’t use their tunnel products, I just expose my ports to only their known IPs. With cloudflare you have a nice “free” waf in front with tls that points to a secure(nginx proxy) or unsecured docker container. Audiobookshelf is a great example. Is best to use their dns product too, for easy management of your public facing sites.
Networking is fun because there are literally infinite potential options. There really isn’t a best option. It’s just what do you prefer. In my case I like to write a docker compose and write a tailscale container into it. I then set the service I want to expose either to my own tailnet or to the internet through funnel or though this other implementation I came up with a while back that I still need to do a write up on. Either way here is a guide i wrote with some docs as reference on my forgejo (git alternative). Docs are kinda a mess but hopefully it makes sense enough to help you out.
I’ve noticed 🥲, which hopefully means there’s no horribly wrong choice. I am leaning towards Tailscale, or maybe WireGuard. Thanks for the docs
I just installed tailscape and it works for my needs. Will read up on your docs. Thx
Lots of great ideas in this thread. It sounds like you prefer Jellyfin, but I always encourage people to consider Plex. Plex is excellent, and even if you prefer the features or interface of Jellyfin, you should never expose any application (Plex, Jellyfin, or otherwise) directly to the Internet. This should be non-negotiable. Plex uniquely solves for external access with the mobile/desktop apps and app.plex.tv by brokering client connections into your network without a NAT/PAT on your router or firewall. Plex also supports Google logins, which means that you can now have 2fa and potentially phishing-resistant 2fa if you secure your Google account with a passkey.
At my company we only expose our applications behind a WAF and firewall, and I see that some folks here have recommended Cloudflare. For those who may not know, it is no longer enough to simply rely on a firewall. When your application is built with components that may become vulnerable over time, it’s critical to use a WAF.
Depending on your level of paranioia. First, you don’t expose your containers, but their port(s).
With a reverse proxy, you will likely expose only 1 port, 443, no matter how many apps/containers/ports it will be pointing internally. For this, having a proper dns setup will be key, and a service like cloudflare dns (not tunnel), which additionally you can proxy your proxy. Also, you will need certificates (letsencrypt) for your traffic to be encrypted. Here, everybody will potentially have access to your services.
Another option is a zero trust tunnel, but as you had seen streaming may break tos. It will be likely enforced if you stream a lot, but I seriously doubt you’ll get any problem by having sporadic one or two users.
Tailscale, you need to add all the devices you need to access your services into the mesh, and you’ll need to re-authenticate every one again every few months.
Setting up a VPN (selfhosted) will require your devices to sign into it when accessing your services, and it seems to me the best approach as this way you will nave the most control over your setting.
Don’t forget to mention that, for this to work, your ISP should provide you with public IP, because if on CGNAT you will have to go with something like tunnels or tailscale.
For Tailscale you can disable key expiry on select devices.
Interested in this as well, posting a comment here just to find the post more easily:)
Twingate is another option if it’s just device to device networking and you trust all the devices that are in your network. It’s free for personal use and peer to peer so no issues with TOS if you’re streaming.