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:
git initcreates a git repo for the current directory and its childrengit add .puts all existing files into the first commitgit 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.
-
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-keygencreates both a private and public key. The public key goes to your server, and the private key stays local to your development machine.-t ed25519is 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. -
ssh-copy-id user@your.serverto add the public key to your server -
ssh user@your.serverto 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.
- Create a
scriptsdirectory from the root of your project, either withmkdiror click-ops - Create a
scripts/deploy.shfile 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.
chmod +x script/deploy.shwill make the script executable./scripts/deploy.shexecutes 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 📦 🧹 🔨 🚀 ✅ ‼️