You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

131 lines
4.9 KiB
Markdown

---
draft: false
title: "Automating Caddy on my DigitalOcean Droplet"
date: "2023-01-05"
author: "Nick Dumas"
authorTwitter: "" #do not include @
cover: ""
tags: ["webdev", "devops"]
keywords: ["", ""]
summary: "Automation ambitions fall flat"
showFullContent: false
---
## Defining units of work
I've got a few different websites that I want to run: this blog, my portfolio, and my about page which acts as a hub for my other sites, my Bandcamp, and whatever else I end up wanting to show off.
To keep things maintainable and reproducible, I decided to stop attempting to create a monolithic all-in-one configuration file. This made it way harder to keep changes atomic; multiple iterations on my blog started impacting my prank websites, and it became harder and harder to return my sites to a working state.
## Proof of concept
The first test case was my blog because I knew the Hugo build was fine, I just needed to get Caddy serving again.
A static site is pretty straightforward with Caddy:
```
blog.ndumas.com {
encode gzip
fileserver
root * /var/www/blog.ndumas.com
}
```
And telling Caddy to load it:
```bash
curl "http://localhost:2019/load" \
-H "Content-Type: text/caddyfile" \
--data-binary @blog.ndumas.com.caddy
```
This all works perfectly, Caddy's more than happy to load this but it does warn that the file hasn't be formatted with `caddy fmt`:
```
[{"file":"Caddyfile","line":2,"message":"input is not formatted with 'caddy fmt'"}]
```
## The loop
Here's where things went sideways. Now that I have two unit files, I'm ready to work on the tooling that will dynamically load my config. For now, I'm just chunking it all out in `bash`. I've got no particular fondness for `bash`, but it's always a bit of a matter of pride to see whether or not I *can*.
```bash
# load-caddyfile
#! /bin/bash
function loadConf() {
curl localhost:2019/load \
-X POST \
-H "Content-Type: text/caddyfile" \
--data-binary @"$1"
}
loadConf "$1"
```
```bash
# load-caddyfiles
#! /bin/bash
source load-caddyfile
# sudo caddy stop
# sudo caddy start
for f in "$1/*.caddy"; do
echo -e "Loading $(basename $f)"
loadConf "$f"
echo
done
```
After implementing the loop my barelylegaltrout.biz site started throwing a 525 while blog.ndumas.com continued working perfectly. This was a real head scratcher, and I had to let the problem sit for a day before I came back to it.
After some boring troubleshooting legwork, I realized I misunderstood how the `/load` endpoint works. This endpoint completely replaces the current config with the provided payload. In order to do partial updates, I'd need to use the `PATCH` calls, and look who's back?
## can't escape JSON
The `PATCH` API *does* let you do partial updates, but it requires your payloads be JSON which does make sense. Because my current set of requirements explicitly excludes any JSON ( for now ), I'm going to have to ditch my dreams of modular code.
Not all my code has gone to waste, though. Now that I know `POST`ing to to `/load` overwrites the whole configuration, I don't need to worry about stopping/restarting the caddy process to get a clean slate. `load-caddyfile` will let me keep iterating as I make changes.
## Proxies
In addition to the static sites I'm running a few applications to make life a little easier. I'll showcase my Gitea/Gitlab and Asciinema configs. At the moment, my setup for these are really janky, I've got a `tmux` session on my droplet where I've manually invoked `docker-compse up`. I'll leave cleaning that up and making systemd units or something proper out of them for a future project.
Reverse proxying with Caddy is blessedly simple:
```
cast.ndumas.com {
encode gzip
reverse_proxy localhost:10083
}
```
```
code.ndumas.com {
encode gzip
reverse_proxy localhost:3069
}
```
With that, my gitea/gitlab is up and running along with my Asciinema instance is as well:
[![asciicast](https://cast.ndumas.com/a/28.svg)](https://cast.ndumas.com/a/28)
## Back to Square One
After finally making an honest attempt to learn how to work with Caddy 2 and its configurations and admin API, I want to take a swing at making a systemd unit file for Caddy to make this a proper setup.
## Finally
Here's what's currently up and running:
- [My blog](blog.ndumas.com)
- [Asciinema](blog.ndumas.com)
- [Gitea](blog.ndumas.com)
I've had loads of toy projects over the years ( stay tuned for butts.ndumas.com ) which may come back, but for now I'm hoping these are going to help me focus on creative, stimulating projects in the future.
The punchline is that I still haven't really automated Caddy; good thing you can count on `tmux`
[![asciicast](https://cast.ndumas.com/a/aWYCFj69CjOg94kgjbGg4n2Uk.svg)](https://cast.ndumas.com/a/aWYCFj69CjOg94kgjbGg4n2Uk)
The final code can be found [here](https://code.ndumas.com/ndumas/caddyfile). Nothing fancy, but troublesome enough that it's worth remembering the problems I had.