Skip to content

Self-Host Jellyfin with Caddy on Windows or Linux

This guide sets up Jellyfin on a Windows or Linux server, gives it a hostname that can follow a changing public IP address, and places Caddy in front to provide HTTPS. The examples keep Jellyfin’s HTTP port private and expose only Caddy to the internet.

You need a server that stays online, administrator access, a media folder, and control of your router or firewall. You can use a domain you own or a No-IP hostname. A dynamic DNS client updates a hostname when your address changes; it does not create public reachability through carrier-grade NAT (CGNAT).


01

Install Jellyfin for Your Operating System

Application Setup

Choose one installation method. Use the official Windows installer for Windows, the official Debian/Ubuntu installation procedure for those distributions, or the official Jellyfin container on another Linux distribution. The container example binds port 8096 to the server’s loopback interface so Caddy on that same host can reach it without publishing Jellyfin directly.

1. Download the current stable installer from https://jellyfin.org/downloads/.
2. Run the installer as an administrator and complete the setup.
3. If offered, enable the Jellyfin Windows service for unattended startup.
4. Open http://127.0.0.1:8096 locally and confirm the setup wizard loads.
❯ View Expected Console Output

Open http://127.0.0.1:8096 on the server. For the container example, ensure the account running Jellyfin can read the media directory; replace UID/GID 1000:1000 if needed.


02

Create an Admin User and Add the Media Library

Initial Configuration

Complete Jellyfin’s browser-based wizard on the server or a trusted device on the same LAN. For a native install, a LAN device can browse to http://<server-LAN-IP>:8096. The container example is loopback-only, so open its wizard on the server itself or use an SSH port forward. Create a unique administrator account, add the media paths, choose your preferred metadata settings, and verify that a test item plays locally before configuring public access.

On Windows, grant the Jellyfin service account read access to the media folders. On Linux, check ownership and permissions for both the media and configuration paths. The Docker example uses read-only access for /media; remove :ro only if Jellyfin must write to that mount.

On the server: http://127.0.0.1:8096
From LAN: http://<server-LAN-IP>:8096 (native install only)
Example library: /srv/media/Movies (Linux)
Example library: D:\Media\Movies (Windows)
❯ View Expected Console Output

Confirm that the library appears and at least one item plays from a device on your home network. Continue only after local access works.


03

Point a Domain or No-IP Hostname at Your Server

DNS and Dynamic DNS

Caddy needs a public hostname for automatic certificate issuance. With a domain you own, create an A record for the server’s current public IPv4 address. Add an AAAA record only when the server has globally reachable IPv6 and your IPv6 firewall allows the required traffic. If your ISP changes your IPv4 address, use your router’s dynamic DNS client or run one updater for your hostname.

With No-IP, create a hostname such as media-room.ddns.net, then configure a No-IP Dynamic Update Client (DUC) or your router to update it. Use a dedicated DDNS Key where the client supports it, keep its credentials private, and configure only one updater for each hostname. See No-IP’s guides for the Windows DUC or Linux DUC.

DNS record: A
Name: media
Value: your current public IPv4 address
Result: media.example.net
Optional IPv6 record, only if publicly reachable:
DNS record: AAAA
Name: media
Value: the server's globally routable IPv6 address
❯ View Expected Console Output

From another network or a public DNS lookup, confirm the hostname returns your current public address before continuing. Some free No-IP hostnames require periodic confirmation; follow the reminder from No-IP to keep the hostname active.


04

Forward Ports 80 and 443 to the Caddy Host

Network Access

Reserve a stable LAN address for the server, then forward inbound TCP ports 80 and 443 on the router to that address. Allow those ports through the server firewall. Port 80 supports HTTP validation and redirects; port 443 serves HTTPS. Do not forward Jellyfin’s port 8096.

If your router’s WAN address differs from the public address shown by an IP-check service, your ISP may be using CGNAT. Ask the ISP for a public address or use a private access method such as a VPN; ordinary port forwarding and dynamic DNS cannot make a CGNAT connection publicly reachable.

TCP 80 -> <server-LAN-IP>:80
TCP 443 -> <server-LAN-IP>:443
Do not add a port-forward for TCP 8096.
❯ View Expected Console Output

Keep the router forwards pointed at the reserved LAN address. If your server’s LAN address changes, the forwards may silently reach the wrong device.


05

Add Caddy to Jellyfin's Known Proxies

Forwarded Headers

Jellyfin uses forwarded headers to recognize the original client and HTTPS scheme. In the Jellyfin dashboard, open Networking, add the address of the Caddy host to Known Proxies, and save. When Caddy and Jellyfin run on the same host with the configuration below, start with 127.0.0.1. Do not trust every proxy with a broad address range.

For a container on a custom Docker network, Jellyfin may see the Docker bridge gateway or another container address instead of 127.0.0.1. Inspect the container network and use the actual source address Jellyfin sees. The loopback-published container in Step 1 is accessed by host Caddy through 127.0.0.1:8096; still confirm the proxy source in Jellyfin’s logs if forwarded client details are not recognized.

Jellyfin Dashboard
-> Administration
-> Networking
-> Known Proxies
-> Add: 127.0.0.1
-> Save
❯ View Expected Console Output

Restart Jellyfin if the setting prompts for it. Keep Jellyfin’s HTTP listener available only on the local host or trusted LAN; the router should expose Caddy, not port 8096.


06

Install the Caddy Web Server

Caddy Setup

Install Caddy using its official package repository on Linux or the official Windows binary. The Linux packages register a systemd service. On Windows, place the binary and configuration in a stable directory; the next step registers it as a service with WinSW.

Terminal window
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo chmod o+r /usr/share/keyrings/caddy-stable-archive-keyring.gpg
sudo chmod o+r /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install caddy
❯ View Expected Console Output

On Linux, the service is named caddy. Run Caddy as a service so it starts after a reboot and can bind to ports 80 and 443.


07

Write and Validate the Caddyfile

Reverse Proxy

Replace media-room.ddns.net with the domain or No-IP hostname from Step 3. The same minimal Caddyfile works on Windows and Linux and sends requests to Jellyfin’s local HTTP listener. Caddy handles the public TLS connection and renews its certificate automatically when DNS and port reachability are correct.

media-room.ddns.net {
reverse_proxy 127.0.0.1:8096
}
Terminal window
sudoedit /etc/caddy/Caddyfile
sudo caddy validate --config /etc/caddy/Caddyfile
❯ View Expected Console Output

A successful validation reports that the configuration is valid. Confirm the hostname is spelled exactly as it appears in public DNS.


08

Run Caddy as a Service and Test from Outside

Launch and Verification

Start the Linux service after validating the configuration. On Windows, download the current WinSW executable for your system from its official releases, place it beside Caddy, and rename it as shown below so it can register Caddy as a service that survives sign-out and reboots. Test from a phone with Wi-Fi disabled or another external network; testing only from the server does not verify router forwarding or public DNS.

Terminal window
sudo systemctl enable --now caddy
sudo systemctl reload caddy
systemctl status caddy --no-pager
sudo journalctl -u caddy -n 50 --no-pager
❯ View Expected Console Output

Browse to https://media-room.ddns.net from outside your LAN. The browser should show a trusted certificate and Jellyfin’s sign-in page. If it fails, check DNS resolution, the public/WAN address, router forwards, host firewall, and Caddy logs before changing Jellyfin’s port.

Jellyfin sign-in page opened at the secure media-room.ddns.net hostname

Figure 1: Jellyfin’s sign-in page reached through the public HTTPS hostname after Caddy’s TLS setup.

Official References

Comments