diff --git a/blog/nixops-services-2020-11-09.markdown b/blog/nixops-services-2020-11-09.markdown
new file mode 100644
index 0000000..1e6333e
--- /dev/null
+++ b/blog/nixops-services-2020-11-09.markdown
@@ -0,0 +1,317 @@
+---
+title: Nixops Services on Your Home Network
+date: 2020-11-09
+series: howto
+tags:
+ - nixos
+ - systemd
+---
+
+# Nixops Services on Your Home Network
+
+My homelab has a few NixOS machines. Right now they mostly run services inside
+Docker, because that has been what I have done for years. This works fine, but
+persistent state gets annoying*. NixOS has a tool called
+[Nixops](https://releases.nixos.org/nixops/nixops-1.7/manual/manual.html) that
+allows you to push configurations to remote machines. I use this for managing my
+fleet of machines, and today I'm going to show you how to create service
+deployments with Nixops and push them to your servers.
+
+[Pedantically, Docker offers volumes
+to simplify this, but it is very easy to accidentally delete Docker volumes.
+Plain disk files like we are going to use today are a bit simpler than docker
+volumes, and thusly a bit harder to mess up.](conversation://Mara/hacker)
+
+## Parts of a Service
+
+For this example, let's deploy a chatbot. To make things easier, let's assume
+the following about this chatbot:
+
+- The chatbot has a git repo somewhere
+- The chatbot's git repo has a `default.nix` that builds the service and
+ includes any supporting files it might need
+- The chatbot reads its configuration from environment variables which may
+ contain secret values (API keys, etc.)
+- The chatbot stores any temporary files in its current working directory
+- The chatbot is "well-behaved" (for some definition of "well-behaved")
+
+I will also need to assume that you have a git repo (or at least a folder) with
+all of your configuration similar to [mine](https://github.com/Xe/nixos-configs).
+
+For this example I'm going to use [withinbot](https://github.com/Xe/withinbot)
+as the service we will deploy via Nixops. withinbot is a chatbot that I use on
+my own Discord guild that does a number of vital functions including supplying
+amusing facts about printers:
+
+```
+ ~printerfact
+ @Cadey~ Printers, especially older printers, do get cancer. Many
+ times this disease can be treated successfully
+```
+
+[To get your own amusing facts about printers, see here or for using its API, call `/fact`. This API has no
+practical rate limits, but please don't test that.](conversation://Mara/hacker)
+
+## Service Definition
+
+We will need to do a few major things for defining this service:
+
+1. Add the bot code as a package
+1. Create a "services" folder for the service modules
+1. Create a user account for the service
+1. Set up a systemd unit for the service
+1. Configure the secrets using [Nixops
+ keys](https://releases.nixos.org/nixops/nixops-1.7/manual/manual.html#idm140737322342384)
+
+### Add the Code as a Package
+
+In order for the program to be installed to the remote system, you need to tell
+the system how to import it. There's many ways to do this, but the cheezy way is
+to add the packages to
+[`nixpkgs.config.packageOverrides`](https://nixos.org/manual/nixos/stable/#sec-customising-packages)
+like this:
+
+```nix
+nixpkgs.config = {
+ packageOverrides = pkgs: {
+ within = {
+ withinbot = import (builtins.fetchTarball
+ "https://github.com/Xe/withinbot/archive/main.tar.gz") { };
+ };
+ };
+};
+```
+
+And now we can access it as `pkgs.within.withinbot` in the rest of our config.
+
+[In production circumstances you should probably use a fetcher
+that locks to a specific version using unique URLs and hashing, but this
+will work enough to get us off the ground in this
+example.](conversation://Mara/hacker)
+
+### Create a "services" Folder
+
+In your configuration folder, create a folder that you will use for these
+service definitions. I made mine in `common/services`. In that folder, create a
+`default.nix` with the following contents:
+
+```nix
+{ config, lib, ... }:
+
+{
+ imports = [ ./withinbot.nix ];
+
+ users.groups.within = {};
+}
+```
+
+The group listed here is optional, but I find that having a group like that can
+help you better share resources and files between services.
+
+Now we need a folder for storing secrets. Let's create that under the services
+folder:
+
+```console
+$ mkdir secrets
+```
+
+And let's also add a gitignore file so that we don't accidentally commit these
+secrets to the repo:
+
+```gitignore
+# common/services/secrets/.gitignore
+*
+```
+
+Now we can put any secrets we want in the secrets folder without the risk of
+committing them to the git repo.
+
+### Service Manifest
+
+Let's create `withinbot.nix` and set it up:
+
+```nix
+{ config, lib, pkgs, ... }:
+with lib; {
+ options.within.services.withinbot.enable =
+ mkEnableOption "Activates Withinbot (the furryhole chatbot)";
+
+ config = mkIf config.within.services.withinbot.enable {
+
+ };
+}
+```
+
+This sets up an option called `within.services.withinbot.enable` which will only
+add the service configuration if that option is set to `true`. This will allow
+us to define a lot of services that are available, but none of their config will
+be active unless they are explicitly enabled.
+
+Now, let's create a user account for the service:
+
+```nix
+# ...
+ config = ... {
+ users.users.withinbot = {
+ createHome = true;
+ description = "github.com/Xe/withinbot";
+ isSystemUser = true;
+ group = "within";
+ home = "/srv/within/withinbot";
+ extraGroups = [ "keys" ];
+ };
+ };
+# ...
+```
+
+This will create a user named `withinbot` with the home directory
+`/srv/within/withinbot`, the group `within` and also in the group `keys` so the
+withinbot user can read deployment secrets.
+
+Now let's add the deployment secrets to the configuration:
+
+```nix
+# ...
+ config = ... {
+ users.users.withinbot = { ... };
+
+ deployment.keys.withinbot = {
+ text = builtins.readFile ./secrets/withinbot.env;
+ user = "withinbot";
+ group = "within";
+ permissions = "0640";
+ };
+ };
+# ...
+```
+
+Assuming you have the configuration at `./secrets/withinbot.env`, this will
+register the secrets into `/run/keys/withinbot` and also create a systemd
+oneshot service named `withinbot-key`. This allows you to add the secret's
+existence as a condition for withinbot to run. However, Nixops puts these keys
+in `/run`, which by default is mounted using a temporary memory-only filesystem,
+meaning these keys will need to be re-added to machines when they are rebooted.
+Fortunately, `nixops reboot` will automatically add the keys back after the
+reboot succeeds.
+
+Now that we have everything else we need, let's add the service configuration:
+
+```nix
+# ...
+ config = ... {
+ users.users.withinbot = { ... };
+ deployment.keys.withinbot = { ... };
+
+ systemd.services.withinbot = {
+ wantedBy = [ "multi-user.target" ];
+ after = [ "withinbot-key.service" ];
+ wants = [ "withinbot-key.service" ];
+
+ serviceConfig = {
+ User = "withinbot";
+ Group = "within";
+ Restart = "on-failure"; # automatically restart the bot when it dies
+ WorkingDirectory = "/srv/within/withinbot";
+ RestartSec = "30s";
+ };
+
+ script = let withinbot = pkgs.within.withinbot;
+ in ''
+ # load the environment variables from /run/keys/withinbot
+ export $(grep -v '^#' /run/keys/withinbot | xargs)
+ # service-specific configuration
+ export CAMPAIGN_FOLDER=${withinbot}/campaigns
+ # kick off the chatbot
+ exec ${withinbot}/bin/withinbot
+ '';
+ };
+ };
+# ...
+```
+
+This will create the systemd configuration for the service so that it starts on
+boot, waits to start until the secrets have been loaded into it, runs withinbot
+as its own user and in the `within` group, and throttles the service restart so
+that it doesn't incur Discord rate limits as easily. This will also put all
+withinbot logs in journald, meaning that you can manage and monitor this service
+like you would any other systemd service.
+
+## Deploying the Service
+
+In your target server's `configuration.nix` file, add an import of your services
+directory:
+
+```nix
+{
+ # ...
+ imports = [
+ # ...
+ /home/cadey/code/nixos-configs/common/services
+ ];
+ # ...
+}
+```
+
+And then enable the withinbot service:
+
+```nix
+{
+ # ...
+ within.services = {
+ withinbot.enable = true;
+ };
+ # ...
+}
+```
+
+[Make that a block so you can enable multiple services at once like this!](conversation://Mara/hacker)
+
+Now you are free to deploy it to your network with `nixops deploy`:
+
+```console
+$ nixops deploy -d hexagone
+```
+
+
+
+
+And then you can verify the service is up with `systemctl status`:
+
+```console
+$ nixops ssh -d hexagone chrysalis -- systemctl status withinbot
+● withinbot.service
+ Loaded: loaded (/nix/store/7ab7jzycpcci4f5wjwhjx3al7xy85ka7-unit-withinbot.service/withinbot.service; enabled; vendor preset: enabled)
+ Active: active (running) since Mon 2020-11-09 09:51:51 EST; 2h 29min ago
+ Main PID: 12295 (withinbot)
+ IP: 0B in, 0B out
+ Tasks: 13 (limit: 4915)
+ Memory: 7.9M
+ CPU: 4.456s
+ CGroup: /system.slice/withinbot.service
+ └─12295 /nix/store/qpq281hcb1grh4k5fm6ksky6w0981arp-withinbot-0.1.0/bin/withinbot
+
+Nov 09 09:51:51 chrysalis systemd[1]: Started withinbot.service.
+```
+
+---
+
+This basic template is enough to expand out to anything you would need and is
+what I am using for my own network. This should be generic enough for most of
+your needs. Check out the [NixOS manual](https://nixos.org/manual/nixos/stable/)
+for more examples and things you can do with this. The [Nixops
+manual](https://releases.nixos.org/nixops/nixops-1.7/manual/manual.html) is also
+a good read. It can also set up deployments with VirtualBox, libvirtd, AWS,
+Digital Ocean, and even Google Cloud.
+
+The cloud is the limit! Be well.
diff --git a/scripts/release.sh b/scripts/release.sh
index 4a77c55..544df55 100755
--- a/scripts/release.sh
+++ b/scripts/release.sh
@@ -7,4 +7,4 @@ kubectl rollout status -n apps deployment/christinewebsite
kubectl apply -f ./k8s/job.yml
sleep 10
kubectl delete -f ./k8s/job.yml
-curl -H "Authorization: $MI_TOKEN" --data "https://christine.website/blog.json" https://mi.within.website/blog/refresh
+curl --http1.1 -H "Authorization: $MI_TOKEN" --data "https://christine.website/blog.json" https://mi.within.website/blog/refresh