How to define fall-back certificate in Nginx if the primary one does not exist?

I have run into this same issue. I am deploying a docker cluster to production for which I want auto-generated SSL certificates. When SSL certs are in place, I want to force all traffic to redirect to https. It's not super glamorous, but this is the solution I came up with.

My nginx server has 2 config files: app.conf and tlsapp.conf

app.conf

server {
    listen 80;
    server_name example.com;
    location /.well-known/acme-challenge/ {
        allow all;
        root /var/www/public;
    }
}

tlsapp.conf

server {
    listen 80;
    server_name example.com;
    location / {
        return 301 https://example.com$request_uri;
    }
}

server {
    listen 443 ssl http2;
    server_name example.com;
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    include /etc/nginx/opt/options-ssl-nginx.conf;
    ssl_dhparam /etc/nginx/opt/ssl-dhparams.pem;
    root /var/www/public;
    index index.php;
    error_log /var/log/nginx/error.log;
    access_log /var/log/nginx/access.log;
    ...
}

I then deploy my nginx container from a custom image.

nginx.Dockerfile

FROM nginx:alpine

COPY docker-config/nginx/conf.d/app.conf /etc/nginx/conf.d/app.conf
COPY docker-config/nginx/conf.d/tlsapp.conf /etc/nginx/conf.d/tlsapp.bkp
COPY docker-config/nginx/opt/ /etc/nginx/opt/
COPY docker-config/nginx/entrypoint.sh /custom-entrypoint.sh

RUN chmod 775 /custom-entrypoint.sh

ENTRYPOINT ["/custom-entrypoint.sh"]
CMD ["/docker-entrypoint.sh"]
CMD ["nginx", "-g", "daemon off;"]

Lastly, my custom nginx image has a custom entrypoint in it. Basically all this entrypoint does is check to see if the ssl certificates have been created yet. If they have, it enables the tlsapp config and disables the config that was only in place to bootstrap in the certificates with certbot.

entrypoint.sh

#!/usr/bin/env sh

set -e

if [ -d "/etc/letsencrypt/live" ]; then
    mv /etc/nginx/conf.d/app.conf /etc/nginx/conf.d/app.bkp
    mv /etc/nginx/conf.d/tlsapp.bkp /etc/nginx/conf.d/tlsapp.conf
fi

exec "$@"

This set up allows my nginx container to start in a new production environment when ssl certs haven't been generated. It provides certbot just enough access to generate the certificates into a shared persistent volume. Then all I have to do is redeploy the nginx container and it will start up, see the certificates, and enable my site's actual config that forces all traffic through https.

I don't love this setup cause it requires an awkward redeploy for a new production env and can be problematic if your set up is more complex than one app against one nginx server, but it works.

Tags:

Nginx