Setting up NGINX and LetsEncrypt on my Home Server

I set up nginx and use LetsEncrypt wildcard certificates using the Route53 plugin to secure my home server's services.
4 min read

Background

My home server’s got quite a few services running on it. I wanted to set up an NGINX reverse proxy so I can give each of the services a memorable hostname instead of remembering port numbers. Additionally, I wanted to ensure that encryption was set up to prevent any nefarious devices on the network from snooping on the traffic and stealing my data.

I did some research on setting Certbot to use the NameCheap API for renewal, which turns out isn’t officially supported. Most of my personal stuff runs on AWS, so I figured I’d point the DNS nameservers there for one of my home network’s domain. I use dynamic DNS to set keep an A record updated on the domain pointing to my home network for remote access.

Then, I could also use Certbot’s Route53 plugin, to automatically generate a wildcard cert for my domain. It would allow me to prevent leaking the names of all my services by using a wildcard, and make it easier to manage.

Certbot Setup

First, I installed on the server Certbot via snap.

# install
sudo snap install --classic certbot

# symlink to /usr/bin
sudo ln -s /snap/bin/certbot /usr/bin/certbot

# configure plugins
sudo snap set certbot trust-plugin-with-root=ok
sudo snap install certbot-dns-route53

You need to make sure your AWS credentials are set up correctly for the Route53 integration to work. I’d recommend doing this by first installing the awscli package, and then running aws configure to set up your credentials under the default profile in the root user.

A few notes on this setup:

  • since Certbot runs as root, you must run sudo aws configure to ensure you set credentials up in the root directory and not your user home directory.
  • The Route53 plugin doesn’t currently accept custom profile names, so it must be under the default profile. This is kind of a bummer.

Next, I then ran the following command to generate a wildcard cert for my domain:

sudo certbot certonly --dns-route53 -d '*.home.example.com'

Nginx

I use docker for all the major services on my server. Nginx is no exception, so I updated my existing nginx config to have separate virtual hosts for each service.

Main Configuration

worker_processes auto;

events { worker_connections 1024; }

http {
    server_names_hash_bucket_size 64;
    include /etc/nginx/sites-enabled/*;
}

Example site configuration

For my homebridge install, this is the configuration file I’m using:

sites-enabled/homebridge.home.example.com

server {
    charset UTF-8;
    listen [::]:443 ssl;
    listen 443 ssl;
    server_name homebridge.home.example.com;

    # Certbot keys
    ssl_certificate /etc/letsencrypt/live/home.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/home.example.com/privkey.pem;

    # SSL config
    include /etc/nginx/ssl.conf;
    ssl_dhparam /etc/nginx/dhparams.pem;

    location / {
        proxy_pass http://127.0.0.1:8581/;
    }
}

Running the server

There’s a couple prerequisites. Since we’re not letting Certbot manage the ssl configuration, we need to download the recommended arguments and also the Diffie-Hellman parameters from Mozilla.

cd $HOME/nginx

# get latest ssl config
curl https://raw.githubusercontent.com/certbot/certbot/master/certbot-nginx/certbot_nginx/_internal/tls_configs/options-ssl-nginx.conf > ssl.conf

# get dhparams
curl https://ssl-config.mozilla.org/ffdhe2048.txt > dhparams.pem

Now, we can actually run the server. I’m using the --network host flag to bind to the host network, which is needed to easily connect to the upstream services which forward ports from their containers.

One thing to note - I’ve got all the nginx config stored in my home directory under nginx/ in this example, so customize the paths to match your setup.

docker run -p 443:443 --name nas-nginx \
  --network host \
  -v $HOME/nginx/nginx.conf:/etc/nginx/nginx.conf:ro \
  -v $HOME/nginx/ssl.conf:/etc/nginx/ssl.conf:ro \
  -v $HOME/nginx/dhparams.pem:/etc/nginx/dhparams.pem:ro \
  -v $HOME/nginx/sites-enabled:/etc/nginx/sites-enabled:ro \
  -v /etc/letsencrypt/:/etc/letsencrypt/:ro \
  -d nginx

One thing to note: I’m mounting all the configuration files & the LetsEncrypt folder as read-only via :ro. This is because I don’t want the container to be able to modify the config files.

If you update the config files, you can just run docker restart nas-nginx to apply the changes.

Additional bind mounts for file server

I have a virtual host setup that is just a file server off of the raid array. To enable this, I add these flags when running the container.

To password protect the site, I also add a htpasswd file which you can generate with htpasswd -c .htpasswd user. I put this file in my home directory under nginx/, and mount it as read-only in the container to allow the config to access it.

These are added with the other bind commands in the run command above

  -v /mnt/raid1:/usr/share/nginx/html:ro \
  -v $HOME/nginx/.htpasswd:/etc/nginx/.htpasswd:ro \

Nginx conf for file server

I use this config to serve the files, along with indexes & the basic http auth.

server {
    charset UTF-8;
    listen [::]:443 ssl;
    listen 443 ssl;
    server_name files.home.example.com;

    # Certbot keys
    ssl_certificate /etc/letsencrypt/live/home.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/home.example.com/privkey.pem;

    # SSL config
    include /etc/nginx/ssl.conf;
    ssl_dhparam /etc/nginx/dhparams.pem;

    location / {
        root   /usr/share/nginx/html;
        autoindex on;

        auth_basic "Restricted";
        auth_basic_user_file  /etc/nginx/.htpasswd;

        include  /etc/nginx/mime.types;
    }
}

DNS

You’ll also need to point your chosen wildcard subdomain to your home server. The quickest solution is to just to give your server a static IP on your local network, then add a wildcard A record to your domain.

For example, you could add the following as a DNS record:

TypeNameValue
A*.home.example.com192.168.1.123

You could also use something like bind9 to set up a local DNS server to direct clients on your LAN to the correct IP address within your network.

Subscribe to my Newsletter

Like this post? Subscribe to get notified for future posts like this.

Change Log

  • 7/19/2024 - Initial Revision

Found a typo or technical problem? file an issue!