
You have something running at home — a media server, a photo backup, a dashboard. It works perfectly on your own Wi-Fi, and the moment you leave the house it’s unreachable.
Getting to it from outside normally needs three things that stop most people: a domain name, a way to keep that name pointed at a home connection whose IP address keeps changing, and a certificate so browsers don’t throw a security warning. This guide does all three for free. It’s the written version of my DuckDNS and Nginx Proxy Manager guide on GitHub, with a couple of traps I’ve added notes for.
By the end you’ll have an address like yourname.duckdns.org, an HTTPS certificate that renews itself, and several services behind one entrance, each on its own subdomain.
The pieces and what each one does
| Piece | Job | Cost |
|---|---|---|
| DuckDNS | Gives you a name and keeps it pointed at your home IP as it changes | Free |
| Nginx Proxy Manager | Sits at the front door and sends each subdomain to the right service | Free, runs in Docker |
| Let’s Encrypt | Issues the HTTPS certificate, from a button inside Nginx Proxy Manager | Free |
| CasaOS (optional) | A web dashboard and app store for the server, so you’re not managing everything over SSH | Free |
Before you start: this opens a door from the internet into your home network. Change every default password, forward only ports 80 and 443, and read the security checklist near the end before you leave it running.
What you need
- A computer or Raspberry Pi to act as the server, running Linux.
- Access to your router’s settings, for port forwarding.
- About an hour.
Step 1: Create your DuckDNS name
- Go to duckdns.org and sign in with GitHub, Google or another account.
- Type the name you want, for example
myserver, and click add domain. Your address is nowmyserver.duckdns.org. - Copy the token shown at the top of the page and keep it somewhere safe. Treat it like a password: anyone who has it can point your name somewhere else.
The single most common confusion: the token is not entered in Nginx Proxy Manager. It’s only used by a small script on your server that tells DuckDNS your current IP address.
Step 2: Keep the name pointed at your home
Most home connections get a new IP address from time to time. This script reports the current one to DuckDNS, and cron runs it every five minutes.
mkdir -p ~/duckdns
echo '#!/bin/bash' > ~/duckdns/duck.sh
echo "echo url="https://www.duckdns.org/update?domains=YOURDOMAIN&token=YOURTOKEN&ip=" | curl -k -o ~/duckdns/duck.log -K -" >> ~/duckdns/duck.sh
chmod 700 ~/duckdns/duck.sh # the token is inside, so only you can read it
~/duckdns/duck.sh
cat ~/duckdns/duck.log # should say OK
Replace YOURDOMAIN with just the name (myserver, not the full address) and YOURTOKEN with your token. Then schedule it:
(crontab -l 2>/dev/null; echo "*/5 * * * * ~/duckdns/duck.sh") | crontab -
crontab -l
Step 3: If you’ll run CasaOS too, move it off port 80 first
This is the trap. Nginx Proxy Manager needs ports 80 and 443 on the server, and CasaOS’s dashboard also uses port 80 by default. If both run on the same machine, one of them can’t start. The fix is to give CasaOS a different port before you start the proxy: edit /etc/casaos/gateway.ini, change port=80 under [gateway] to something free such as port=8080, then restart it:
sudo nano /etc/casaos/gateway.ini
sudo systemctl restart casaos-gateway
CasaOS is now at http://YOUR_SERVER_IP:8080. If you haven’t installed CasaOS yet, see my guide to turning a spare computer into a home server, or skip this step.
Step 4: Install Nginx Proxy Manager
Install Docker, then create a folder and a Compose file for the proxy:
sudo apt update
sudo apt install docker.io docker-compose -y
mkdir -p ~/nginx-proxy-manager && cd ~/nginx-proxy-manager
nano docker-compose.yml
services:
app:
image: 'jc21/nginx-proxy-manager:latest'
restart: unless-stopped
ports:
- '80:80' # web traffic
- '443:443' # HTTPS traffic
- '81:81' # the admin panel - never forward this one on your router
volumes:
- ./data:/data
- ./letsencrypt:/etc/letsencrypt
docker-compose up -d # on newer Docker installs: docker compose up -d
Open http://YOUR_SERVER_IP:81 from a computer on your network and sign in. The default login has long been [email protected] with the password changeme; newer versions may ask you to create the first account instead. Either way, set your own email and a strong password straight away.
Step 5: Forward ports 80 and 443 on your router
In your router’s admin page (often 192.168.1.1 or 192.168.0.1), find Port forwarding — sometimes under Advanced or NAT — and create two rules: external port 80 to your server’s IP on port 80, and external 443 to your server’s IP on 443. Give the server a fixed local IP first (a DHCP reservation in the router), or the rules will break when its address changes.
Step 6: Add a proxy host and a free certificate
- In Nginx Proxy Manager open Hosts → Proxy Hosts → Add Proxy Host.
- Domain Names:
myserver.duckdns.org. Scheme: http. Forward Hostname/IP: your server’s local IP. Forward Port: the port of the service you want to reach. - On the SSL tab choose Request a new SSL Certificate, enter your email, accept the terms, tick Force SSL, and save.
- Visit
https://myserver.duckdns.org— you should see the padlock.
Step 7: One subdomain per service
DuckDNS answers for every name under yours as well, so casaos.myserver.duckdns.org or photos.myserver.duckdns.org work without any extra DNS setup. Add one proxy host per service — for CasaOS: domain casaos.myserver.duckdns.org, forward port 8080 (from step 3) — and request a certificate for each one.
How a request travels once it’s all running: DuckDNS turns the name into your home IP → your router forwards ports 80/443 to the server → Nginx Proxy Manager reads the subdomain and passes the request to the right service → the certificate keeps it encrypted end to end.
Troubleshooting
- Works at home, not outside. Test from your phone on mobile data, not Wi-Fi. Check
cat ~/duckdns/duck.logsaysOK, check both port-forwarding rules, and check whether your internet provider blocks port 80 or 443 — some do. - The certificate request fails. Let’s Encrypt has to reach your server on port 80 from the internet to prove you own the name. Make sure port 80 is forwarded and that the name already points at your current IP.
- Is the proxy even running? In the
~/nginx-proxy-managerfolder rundocker-compose ps, anddocker-compose logs appfor its log. (Compose names the container after the folder and service, so a plaindocker logs nginx-proxy-managerwon’t find it.) - Something else is using the ports.
sudo ss -tulpn | grep -E ':80 |:443 |:81 'shows what’s listening — often CasaOS, see step 3. - DuckDNS stopped updating. Check the token in the script, that the script is executable, and that the cron line exists with
crontab -l.
Security checklist before you walk away
- Every default password changed — Nginx Proxy Manager, CasaOS and each app.
- Only 80 and 443 forwarded. Port 81 (the proxy’s admin panel) and SSH stay private.
- Only publish services that need to be reachable. An Access List in Nginx Proxy Manager can put a password in front of anything sensitive.
- Keep the server and the containers updated, and keep backups.
Port forwarding or a tunnel?
This website doesn’t use any of the above. It reaches the internet through a Cloudflare Tunnel instead, which I explain in how I run this website on a Raspberry Pi 4. Both approaches are valid:
| DuckDNS + port forwarding | Cloudflare Tunnel | |
|---|---|---|
| Router changes | Forward ports 80 and 443 | None — the server connects out |
| Your own domain needed | No — a free duckdns.org name | Yes, managed in Cloudflare |
| Home IP visible | Yes | No |
| Works when the ISP blocks ports | No | Yes |
| Who sits in the middle | Nobody — traffic comes straight to you | Cloudflare |
Frequently asked questions
Is DuckDNS really free?
Yes. It’s a free dynamic DNS service; you sign in with an existing account and get your subdomains.
Do I need a static IP from my internet provider?
No — that’s exactly what the update script in step 2 is for. It keeps your name pointed at whatever IP you have today.
Do I put the DuckDNS token in Nginx Proxy Manager?
No. The token only goes in the update script. Nginx Proxy Manager only needs the domain name.
Can I run all of this on a Raspberry Pi?
Yes. Docker, Nginx Proxy Manager and CasaOS all run on a 64-bit Raspberry Pi OS.
The full guide, with every command in one place, is on GitHub. If it saves you time, a star there helps other people find it.
