Easy SSL Websites with Docker and LetsEncrypt
Like I mentioned in the previous post about a self-hosted life, I’ve grown fond of Docker for website management. There’s something very satisfying about running some commands and getting the same result every time (i.e., purely functional) and knowing there’s no stateful cruft hanging around from previous iterations. Enter Docker.
Docker creates containers that are reproducible based on a file that tells it exactly what to do so that it does the same thing every time. So for something like Nextcloud, its Dockerfile tells it how to install itself with specific sources and commands and so it should produce the same output every time you process the Dockerfile.
There are a few reasons why this is useful from a website administration perspective.
Easy Deployment
It’s relatively easy to deploy things. For example, Nextcloud. A normal Nextcloud install would require spinning up a webserver like Nginx, a database like MySQL, putting all of the proper files in their proper locations, and then configuring things how you like them. And that’s just Nextcloud.
When you’ve got Nextcloud, Firefly III, Gitea, Hugo, and more, it’s nice to have all of the installation and configuration done by Docker instead of by hand.
Easy SSL
Using a LetsEncrypt Nginx proxy container, it’s easy to get SSL certs for each of your subdomains and have them renew automatically without any hassle. You just configure Docker appropriately once and you’re good to go.
Easy Updating
Normally you’d have to manually update all of your services, hoping nothing breaks when you do. Not to mention updating your actual server itself and all of the underlying packages that go with it (e.g., if you’re running on Ubuntu and do an apt upgrade, fearing nothing breaks your site).
With Docker (specifically Docker Compose) you tell it to update without fear of anything breaking because the images have everything they need to run inside of them and have already been tested by the maintainer. Aside from maybe a database (which you can also Dockerize), they’re self-contained.
Easy Backups
If you properly set up Docker to use volumes for each of your containers, then all of their stateful information (i.e., the data that must persist across deletions and creations of your container) can be put onto a separate drive or partition which can be backed up along with the Docker Compose files. You could then wipe your entire machine, remount the partition, install Docker, run a single command, and everything will be back the way it was before.
How?
Let’s use Nextcloud as an example.
I’ll assume that you can figure out how to install Docker and Docker Compose, that you can read the Docker docs if you’re confused, and that you know how to create a domain name and make it point to the IP address of your server. I primarily just to want to show how to integrate Let’s Encrypt with Nextcloud, because that was the biggest hurdle for me.
First you need to get nginx-proxy which will work in concert with the other containers to properly direct requests to different containers:
|
|
Then you need to create a Docker Compose file that describes Nextcloud, its database, and the SSL helpers. Here’s an example named docker-compose.yml.
- {YOUR_IP} should be replaced with the IP of your server.
- {PASSWORD_1}, {PASSWORD_2}, and {PASSWORD_3} should be replaced by different (secure) passwords.
- {DOMAIN_NAME} should be replaced with the domain name of your server.
- {YOUR_EMAIL} should be replaced with the email address that you want associated with your SSL cert.
|
|
This makes use of five different Docker images:
nginx, docker-gen, and letsencrypt-nginx-proxy-companion all work together to not only request (and renew) SSL certs for each container’s domain name, but also to direct different domain names to different Docker containers. So a request to {DOMAIN_NAME} will be directed to the Nextcloud container.
nextcloud is the container with all of the required Nextcloud files.
mariadb is the database that the Nextcloud container uses.
A few things to notice:
Versioning. I used specific version tags with every image so that they’re pinned to a specific release. That way I can fix the versions at ones that I know work and update them explicitly if I wish.
Volumes. In this example, your separate partition would be mounted at /volumes, and each of the containers would put their stateful data there (Docker will create the subdirectories itself). Then if you destroy a container and create a new one (like during an update), all of the persistent data remains. /volumes would be what you backup, not needing to worry about the actual Ubuntu install at all. All you need is Docker.
Databases. The nextcloud-db container creates a mariadb database and the nextcloud container is hooked up to that database with the environment variables starting with MYSQL_. Some images might not be this nice and instead ask for your database info when you first run them, in which case you would still create a database like this but then pass the relevant database name, user, password, and host through the web interface.
With that, you can then run:
|
|
This will download all of the images and then create the containers. It takes some time to request the certs but after a few minutes you should then be able to go to https://{DOMAIN_NAME} and it will load up Nextcloud.
More
That’s just the beginning. You can leave the nginx, docker-gen, and letsencrypt-nginx-proxy-companion containers untouched and then continue to add on more containers for other services you want (e.g., Gitea, Firefly, etc). Most services have been Dockerized, either officially or by someone else. Just make sure you have:
|
|
and
|
|
for each container you add.
Backing Up for DigitalOcean
If you’re using DigitalOcean and have all of your persistent data (i.e., your Docker volumes) on a DigitalOcean storage volume, you can take periodic snapshots of that volume in case something ever goes wrong. You could do it manually whenever, but I’m lazy so I wrote a Python script that uses a library to connect to DigitalOcean’s API and take a snapshot if it’s been over a week since the last snapshot:
|
|
Last Edited: Dec 20, 2022