Exposing your services using subdomains with a dynamic ip
Tags: webdev, networking, cloud
Published: 2024-01-13 23:32:30

## Prerequisites
- This guide assumes that you using Docker and Docker compose to manage your self hosted services, Caddy can be installed without Docker but ddns-updater have to be replaced with another service.

## How will this work?
- I'm glad you asked, we will use two containerized tools. The first is [ddns-updater](https://hub.docker.com/r/qmcgaw/ddns-updater "sdsd") which will update your [A records](https://developers.cloudflare.com/dns/manage-dns-records/reference/dns-record-types/) (subdomains) when your ip has changed, so your domains will always point to your server. 
- The second is [Caddy](https://hub.docker.com/_/caddy) a reverse proxy which will forward a request for a subdomain to a port on your machine and it will give you automatic HTTPS.

## Why do i need a reverse proxy?
- You may ask why do you need a reverse proxy if you know the port of your service and have a domain from which you can access it?
1. Due to the automatic HTTPS your traffic will be encrypted, without SSL (HTTPS) everyone on your WIFI network and your ISP can read your traffic.
2. You only need to open two ports on your router 443 (HTTPS), 80 (HTTP).
3. You can do a lot of additional things between the client sending a requests for a site and and sending a response using a middleware for example, you can do additional authentication for an unprotected site with a [caddy plugin](https://github.com/greenpau/caddy-security/blob/main/README.md).

## Setting up the domain and subdomains
- First you have to get a domain, you can get a free domain on [Freenom](https://www.freenom.com/en/index.html?lang=en) but I don't recommend it. Many people reported on Reddit that they domain's were removed.
- You can get a free sub domain with [DuckDNS](https://www.duckdns.org/), in this case you can setup your sub domains in a form of "sub sub domain", for example if you clam the *mydomain.duckdns.org* subdomain, your service would be located at *service.mydomain.duckdns.org*, but i haven't tried this option but people on Reddit reported success with it. 

## Manage domain with Dynamic DNS service
- This service can manage your domain, your domain provider will provide you with an API key or token which ddns-updater can use to update the domain.
- Please check if your provider is on the supported list for ddns-updater.
- It also provides a web UI from you can see the status of you domains.
![Web UI](https://raw.githubusercontent.com/qdm12/ddns-updater/master/readme/webui.png)

#### Setting up ddns-updater
- Docker compose setup:
```yaml
Dynamic-dns:  
   image: qmcgaw/ddns-updater  
   container_name: dynamic-dns  
   restart: unless-stopped  
   privileged: true  
   volumes:  
     - /your/path/:/updater/data  
   ports:  
     - 8000:8000
```
- The configuration stored in the config.json in the specified data folder.
- Every subdomain has an entry in the settings list with the domain provider, the domain, the sub domain and the api key.
```json
{
"settings":[
      {
         "provider":"namecheap",
         "domain":"domain.cc",
         "host":"sub",
         "password":"apikey",
         "provider_ip":true
	  }
	]
}

```

## SAetting up Caddy
- Docker compose setup:
```yaml
caddy:  
   container_name: caddy  
   image: caddy:latest  
   restart: always  
   ports:  
     - 443:443/tcp  
     - 80:80/tcp  
   volumes:  
     - /your/path/caddy_config:/config:rw  
     - /your/path/site:/srv:rw  
     - /your/path/caddy_data:/data:rw  
     - /your/path/Caddyfile:/etc/caddy/Caddyfile:rw

```
- Configuration stored in the Caddyfile, for every subdomain you need to define a reverse_proxy ip:port, you can write comments with hashtags:
```json
sub.example.cc {  
       encode gzip  # forward header
               log {  
	               output file /data/monitor.log {  
	                   roll true # Rotate logs, enabled by default  
                       roll_size_mb 5 # Set max size 5 MB  
                       roll_gzip true# Whether to compress rolled files  
                       roll_local_time true # Use localhost time  
                       roll_keep 2 # Keep at most 2 log files  
	                   roll_keep_days 7 # Keep log files for 7 days  
                       }  
               }  
   reverse_proxy ip:port  
}
```
## Example with Namecheap domain provider
- if you want to create a self hosted netflix with the [Jellyfin](https://jellyfin.org) thats on the *8096* port with a domain from Namecheap
1. Create an A record for the subdomain for the service, for example  *jellyfin.mydomain.com* on your domain providers dashboard, in Namecheap navigate to Account->Dashboard->Domain List->Manage->Advanced DNS. The "Type" enter 'A + Dynamic DNS Record' for "Host" enter the sub domain-> 'jellyfin', and Value is the your ip, set the [TTL](https://developers.cloudflare.com/dns/manage-dns-records/reference/ttl/) (update interval for the record) to Automatic
2. Add an entry to the settings list in the config.json for the ddns updater:
```json
      {
         "provider":"namecheap",
         "domain":"mydomain.com",
         "host":"jellyfin",
         "password":"apikey",
         "provider_ip":true
	  }
```
3. Add an entry in the Caddyfile:
```json
jellyfin.mydomain.com {  
       encode gzip  # forward header
               log {  
	               output file /data/jellyfin.log {  
	                   roll true # Rotate logs, enabled by default  
                       roll_size_mb 5 # Set max size 5 MB  
                       roll_gzip true# Whether to compress rolled files  
                       roll_local_time true # Use localhost time  
                       roll_keep 2 # Keep at most 2 log files  
	                   roll_keep_days 7 # Keep log files for 7 days  
                       }  
               }  
   reverse_proxy 192.168.0.101:8096
}
```
4. Enjoy!