Rsync deploy on commit

Why endure the tedium of uploading files?

Note: This site is built in 11ty, but I'll write this up relatively generally. You (and your agent, presumably) should be able to make it work, but forgive any 11ty-specific language I use.

The only thing better than automating a process is triggering that automation automatically through something you were going to have to do anyway. When I make changes to this site, there are things that I specifically need to do and things that I do not need to do. I wish to do zero of the things that I do not need to do.

Things that I specifically need to do:

  • type changes (the text on this site is 100% human-generated)
  • save changes (I'm not sure I want my IDE to auto-save anyway)
  • check in and commit changes (thank goodness I can do both in one step)

Things that I do not need to do and therefore do not want to do:

  • clean up the dist directory (11ty does not automate this but should)
  • build the site into the dist directory
  • upload files to my web host
  • type my password or some crap

As a software engineer, those latter three items are excruciating. Why do anything that the computer is perfectly capable of doing on its own? Ever?? Let's automate them.

Before we go any further, let's address my assumptions so you can be sure this applies to you:

  • You are using some SSG that creates a build directory for deployment
  • You deploy via SFTP (most likely) and can use Rsync (which is more good for automations)
  • You deploy from the machine where you actually write the code and don't have some convoluted process in between

That you? Great. Let's go.

Initialize your repository

Assuming you already have Git installed, there are but 3 things to do:

  1. git init creates a git repo for the current directory and its children
  2. git add . puts all existing files into the first commit
  3. git commit -m "Initial commit" commits your project with an "Initial commit" message

Prepare and test passwordless login

If this doesn't work for any reason, get it to work. If you can't, for any reason, you'll be typing your password in every time you deploy. It's still better than manually typing a command only to have to type your password after, but doing neither is best.

  1. ssh-keygen -t ed25519 -C "your.server deploy" to create a SSH key that you can use for this process and never have to type your password again (if this works on the first try)

    ssh-keygen creates both a private and public key. The public key goes to your server, and the private key stays local to your development machine.

    -t ed25519 is short for "type edward twenty-five thousand five hundred nineteen" which invokes an extremely futuristic lad named edward to secure your stuff better than RSA ever could. But choose a different algorithm for the keygen if you know better and want to. Just remember: edward is watching. If you ever encounter edward in the wild and attempt to discuss this with him, he may argue that his real name is "Curve25519-based EdDSA signature" ... do not fall prey to this ruse.

    -C "your.server deploy" is just adding a human-readable comment to the key so that you have some idea what on earth you used it for when you next think about it in 7 years.

  2. ssh-copy-id user@your.server to add the public key to your server

  3. ssh user@your.server to test that you can connect to SSH without a password now

Create a deploy script

This might be the most specifically 11ty section. I've done Astro and Nuxt static sites, but not recently, and I have no intention of looking up and accommodating all the variance, especially when you can just ask some rando named Claude to do it for you.

  1. Create a scripts directory from the root of your project, either with mkdir or click-ops
  2. Create a scripts/deploy.sh file much in the same fashion (whichever is your preference)
#!/usr/bin/env bash

set -e

echo "🧹 Cleaning dist…"
rm -rf dist/

echo "🔨 Building site…"
npx @11ty/eleventy

echo "🚀 Uploading to server…"
rsync -avz --delete \
  --exclude='.DS_Store' \
  -e "ssh -i /Users/user/.ssh/your_key" \
  dist/ \
  user@your.site:/home/user/site

echo "✅ Deployment complete."

Let's actually explain these as well.

Command Meaning
#!/usr/bin/env bash "Shebang! This is a bash script, and you should run it as such!"
set -e tells bash to kill this script on any error (since each step relies on the success of the prior)
echo {whatever} I will not be explaining the echo lines, but remember that emoji are mandatory in CLI responses.
rm -rf dist/ empties the distribution directory. This is important, because 11ty naively generates all the new stuff you create but does nothing about things you ultimately remove from your project. And it's not like we want to clean things up manually. Ew. This is fine, but will take more time if your project starts to get huge.
npx @11ty/eleventy finds your node modules and runs the 11ty package, which is where all the code for the build process lives.
rsync is the command that synchronizes files on a remote server. No one knows where it got its mysterious name, though.
-a "archive" flag ensures things are copied faithfully. man rsync for more.
-v "verbose" prints everything that's happening and is optional once you trust the process
-z Compresses the data on transfer
--delete Removes anything on the remote server that is not included in your distribution directory (this is the feature that 11ty build should have but doesn't, necessitating us to do the rm command)
\ these just mean the command continues on the next line, so I'm done explaining them
--exclude='.DS_Store' \ If you're on a Mac, definitely don't upload these hidden files. You can exclude anything you want in this way.
-e "ssh -i /Users/user/.ssh/your_key" \ passes specific configuration to rsync, in this case, ssh, instructing it to use your key
dist/ \ The contents of the dist/ folder (not the folder itself) are what should be deployed to the destination.
user@your.site:/home/user/site SSH account and destination address, where the site is hosted.

Make it executable

This one's quick and straightforward. A single command gets it done, and there are two ways to test and verify it worked.

  1. chmod +x script/deploy.sh will make the script executable
  2. ./scripts/deploy.sh executes the script directly

Note that npm build deploy leverages npm to run the script, but this will not test the executability of the script. This command can work and the next step will fail if your script is not executable.

Automate deployment on commits

Committing to git should be done with a commit message, a step I am happy to do manually and thoughtfully. Running the deployment script afterwards is tedious, thoughtless, and easily forgettable. Let's automate it so we never have to think about it again (and then write up a post with instructions because we will have no recollection of the process the next time we want to recreate it for another project).

Create a post-commit file in the .git/hooks/ folder however you like. e.g. vi .git/hooks/post-commit

#!/usr/bin/env bash

echo "📦 Commit detected — publishing Hauskanpito…"

./scripts/deploy.sh

You know how this works, now. We've got the shebang, the echo (with mandatory emoji), and the script execution. That's it. Because we put it in the git/hooks, it will automatically run after commit. Well, it will once we make the post-commit file itself executable.

chmod +x .git/hooks/post-commit

Conclusion

You're ready to make a change to the site, save it, and commit it. Once you do, if you've done everything properly, your site will auto-deploy to your host. And even if you have so much AI brainrot that you don't read the output, your sequence of emoji will tell you what's happening.

Congratulations on your 📦 🧹 🔨 🚀 ✅ ‼️