Deploying an Astro site through Sourcehut
I was previously less interested in Astro because I thought it was mostly a tool for building websites using an islands architecture, something we’re extensively trialling at work. But having read the excellent Astro docs, I realised that Astro websites are “Zero JS, by default”, and that islands are simply an optional, albeit useful feature. With Astro under the hood, this website ends up just as small and static as it was when I deployed it using my self-designed static site generator, Orogene.
With the move to a new framework and a new git repository, I also thought to make use of my Sourcehut account and set up an automatic deployment script using Sourcehut’s superb build.sr.ht build system. With a deployment system in place, my workflow will look like the following:
- Write a new post, like this one, on my local machine
- Commit it to this site’s Sourcehut repository
- Sourcehut will spin up a virtual machine, install Astro, build the static site from the latest commit in the repository, and copy the newly minted static site directly to my server - all without my lifting a finger.
In my research, I found a brilliant post which documented a similar process, but for the static site generator Hugo. I’ve borrowed extensively from that post - mostly for my own reference - the steps needed to create a secure path between Sourcehut’s build system and my server, which will prevent my server from being compromised if the private key is leaked. I strongly recommend you read the original post for the context.
Create SSH keypair
We’ll be creating a user,
homepage_deployer, on the remote server, who will be allowed only to upload files to a specific directory. That user needs an SSH key to log in to the server from inside the build job. Create a keypair on your local machine:
ssh-keygen -t ed25519 -f ~/.ssh/homepage_deployer
Copy the contents of the public key,
~/.ssh/homepage_deployer.pub, for the next step.
This is all borrowed from sidhion.com:
# On the server: useradd homepage_deployer # Lock the password because we won't be using it. passwd -l homepage_deployer mkdir -p /home/homepage_deployer/.ssh # Manually add the contents of homepage_deployer.pub to the authorized_keys file. vim /home/homepage_deployer/.ssh/authorized_keys chown -R homepage_deployer:homepage_deployer /home/homepage_deployer
Next, we make use of a script called rrsync, which is a restricted
rsync designed for scenarios where a user should only be permitted to rsync in a specific directory on a remote server. The script is inside rsync’s directory and needs to be moved to a location on the
cp /usr/share/doc/rsync/scripts/rrsync /usr/local/bin/rrsync
authorized_keys file and add the following text at the beginning of the line with the public key, separated from the key by a space:
restrict,pty,command="/usr/local/bin/rrsync -wo /var/www/raphaelkabo.com"
restrict prevents all forwarding for this key and restricts tty allocation;
pty re-enables tty allocation (since we need one for the script to run); the final option,
command=, will simply execute the provided command whenever this key is used for authentication, ignoring any supplied commands. All these options are documented in the sshd manpages.
rrsync flags tell the script that it is only allowed to write to the directory
/var/www/raphaelkabo.com, which is where my homepage lives on the server.
The final step is to ensure that
rsync has access to the directory where it needs to work and its parent directory, if need be. I ran into some issues with this: non-fatal errors caused by
rsync failing to save modification times caused the Sourcehut build script to register a failure. Simply assigning
homepage_deployer to the group which owned
/var/www/raphaelkabo.com was not enough; I had to make
homepage_deployer the owner of
/var/www/raphaelkabo.com and all the files inside it:
chown -R homepage_deployer /var/www/raphaelkabo.com chmod 755 /var/www/raphaelkabo.com
Add the contents of the private key you created,
~/.ssh/homepage_deployer, to Sourcehut’s secrets page. If this key is leaked, it will only allow the attacker to
rsync write into one directory on the server - hardly the end of the world. Make a note of the UUID which Sourcehut generates for the secret.
Sourcehut’s build system is controlled by a build manifest, such as a file called
.build.yml located in the root directory of the repository. This is a pleasantly simple way to orchestrate a build task and I am a big fan. When the
.build.yml file is commited to the repository, Sourcehut immediately starts its first build, and will then trigger a build using the latest contents of
.build.yml on every commit.
As easy as the system might be, I did have to run my builds about twelve times before I got this right, just to save you the trouble. Here is my final build manifest to build and deploy an Astro site using
image: alpine/edge packages: - rsync - nodejs - npm sources: - https://git.sr.ht/~lown/homepage secrets: - a5e2fe34-6fce-48d3-be7b-484400a4ab04 tasks: - install-build-deps: | wget -qO pnpm "https://github.com/pnpm/pnpm/releases/latest/download/pnpm-linuxstatic-x64" chmod +x pnpm sudo mv pnpm /usr/bin - build: | cd homepage pnpm install pnpm astro build - deploy: | cd homepage rsync -rvz -e "ssh -o StrictHostKeyChecking=no -i ~/.ssh/a5e2fe34-6fce-48d3-be7b-484400a4ab04" dist/ email@example.com:/
This file instructs Sourcehut to do the following things:
- Spin up an Alpine Linux virtual machine
- Clone the latest from my repository into a folder called
homepagein the working directory
- Fetch my secret SSH key and set it up
- Download the
pnpmsources for Alpine Linux and move them into a location on the
- Set up and build my website using Astro
- Deploy it via
rsyncto my server.
As the original blog post notes (with my alterations):
-o StrictHostKeyChecking=nois required, otherwise the build job will hang because it doesn’t recognize the server to connect to (and will ask for confirmation before proceeding). […] Also important is the destination of the rsync command:
firstname.lastname@example.org:/. Notice how the path only specifies /. This is because back in the server, we told rrsync to use /var/www/raphaelkabo.com as the root path of any file synced with rsync.
Once I got my
rsync permissions issues ironed out, this system worked flawlessly for me. I found the Sourcehut build system very pleasant and easy to use, and I can now publish to my website with a simple commit, which is lovely. It complements Astro’s design philosophy nicely: ‘get out of the developer’s way as much as possible to let them do work without fiddling with tooling for weeks’.
I still think you should write a static site generator, though. ↩