# Exercise: Deploy Over SSH

Give the book club's server a short name in an SSH config file, then write deploy.sh, one command that checks the site, copies it to the server with rsync over SSH, lists what's live with ssh and can rehearse first, and let a check script that needs no server tell you what's done.

Freelancer · Unix · Linux · Updated November 10, 2026

The Maple Street Book Club's website is a folder of four files, and it lives on a server called `bookclub`. Every time someone changes a page, somebody has to put it live: copy the right files to the right folder, remove the page the club took down, and check what's there now. Done by hand, one of those steps gets forgotten. You'll make it one command: give the server its short name in `ssh-config`, then write `deploy.sh`, which checks the site, copies it with `rsync` over SSH, lists what's live with `ssh`, and can rehearse first. A check script tells you what's done, and it needs no server at all.

## Before you start

You need a terminal: the Terminal app on a Mac, or Ubuntu on Linux or in WSL on Windows. `ssh` and `rsync` are already there, and the check runs with **bats**, a small program that runs checks written for bash. If `bats --version` or `rsync --version` doesn't print a version number yet, install them:

```bash
sudo apt install bats rsync      # Ubuntu, and Ubuntu in WSL
brew install bats-core           # a Mac, with Homebrew
```

`sudo` asks for your password, the one you use to sign in; nothing shows on the screen while you type it.

## Get the exercise code

Open this lesson's GitHub repository, [github.com/ask-codey/exercise-bash-deploy-over-ssh](https://github.com/ask-codey/exercise-bash-deploy-over-ssh), also linked from the **Exercise** box beside the lesson. Click **Code**, then **Download ZIP**, and unzip it into your `terminal-practice` folder. Or clone it with Git. On Windows, put it inside Ubuntu, in your Linux home folder, not in a Windows folder under `/mnt/c`, so `chmod` works on the scripts.

The folder holds:

- `site/`: the book club's website, the pages you deploy.
- `ssh-config` and `deploy.sh`: the two files you write. Each starts with a comment that says exactly what it must do.
- `stand-in/ssh`: a pretend `ssh`, for trying your script on a computer with no SSH server.
- `check.bats`: the check. You don't need to change it.

## What to do

Open a terminal in the exercise folder, make the scripts runnable (*Your First Script*), and open the first file in nano:

```bash
chmod +x deploy.sh stand-in/ssh
nano ssh-config
```

1. **`ssh-config`**: a `Host` block that gives the name `bookclub` the `HostName localhost`, your own user name as `User` (`whoami` prints it), and the `IdentityFile ~/.ssh/id_ed25519`. These are the lines for your `~/.ssh/config`.
2. **`deploy.sh`** copies what's inside `site/` into the folder `bookclub-site` on the server `bookclub`, with `rsync` over SSH, so the server's copy matches `site/` exactly: a page deleted from `site/` is deleted from the server too. Then it prints `Live on bookclub:` and the files in `bookclub-site` on the server, by running `ls` there with `ssh`.
3. **`deploy.sh -n`** rehearses: `rsync` lists what it would copy and changes nothing, then the script prints `Rehearsal: nothing was copied.` and stops with exit code 0.
4. **No `site/index.html`, no deploy.** If it's missing, `deploy.sh` says so, copies nothing and stops with exit code 1, so an empty or wrong `site/` can never replace the live site.

Try it as you go. With no SSH server, the stand-in runs every `ssh` command on your own computer, in the folder `stand-in/server-home/`; it's first on the `PATH` for this one command only (*PATH: How a Command Is Found*):

```bash
PATH="$PWD/stand-in:$PATH" ./deploy.sh -n
```

When every check passes, you can also deploy for real: see *Optional: deploy to a real server* below.

## Check your work

From the exercise folder:

```bash
bats check.bats
```

At first almost every check fails. That's expected: each job you finish turns its checks into a ✓. The check reads `ssh-config` with your own `ssh -G`, which connects to nothing, and runs a copy of `deploy.sh` on a made-up garden club site of its own, in a throwaway folder, with its own stand-in `ssh`. A failing check says what to look at, for example *about.html was deleted from site/, but it's still live on the server after the next deploy.*

## Hints

- **Check the config before you trust it.** `ssh -F ssh-config -G bookclub` shows the settings ssh would use, as in *SSH Keys and ~/.ssh/config*. If it says `hostname bookclub`, ssh didn't find your `Host` block.
- **Mind the slash and the colon.** *Copying Between Computers: scp and rsync* shows what `site/` and `site` each copy, and what the colon in `bookclub:bookclub-site/` does.
- **Rehearse.** The option that makes `rsync` only list what it would copy is the one you rehearsed with in *Backups That Work: rsync*. `${1:-}` is the first argument, or nothing when there isn't one.
- **Which computer fills it in?** *Running Commands on Another Computer* shows which quotes let your script's `$folder` reach the server already filled in.
- **Check first, copy second.** *Checking Before You Act: File Tests* shows how to test that a file is there before anything else runs.

## When you're done

Every check passes:

```text
check.bats
 ✓ deploy.sh, ssh-config and site/ are here, and so are ssh and rsync
 ✓ 1) ssh-config gives bookclub the HostName localhost
 ✓ 2) ssh-config gives bookclub a User
 ✓ 3) ssh-config gives bookclub the key ~/.ssh/id_ed25519
 ✓ 4) deploy.sh copies what's inside site/ into bookclub-site on the server
 ✓ 5) deploy.sh connects to the short name bookclub
 ✓ 6) deploy.sh lists what's live on the server, with ssh
 ✓ 7) deploy.sh makes the server's copy match site/: a deleted page goes too
 ✓ 8) deploy.sh -n rehearses and changes nothing on the server
 ✓ 9) deploy.sh stops with exit code 1, and copies nothing, when site/index.html is missing
 ✓ 10) deploy.sh copies with rsync -a and --delete, and runs a command with ssh

11 tests, 0 failures
```

Now look back at `deploy.sh`. If the club moved its site to a new server tomorrow, which file would you change, and how many lines?

## Optional: deploy to a real server

The stand-in pretends. A real SSH server shows you the whole trip, and your own computer can be that server, as in *Logging In to Another Computer: ssh*. This step is optional: the check and your submission don't need it.

Turning on an SSH server changes your computer: anyone who can reach it over the network can try to log in with your user name and password. Turn it on for this step, and off again when you're done.

1. **Turn the SSH server on.** On Ubuntu, and Ubuntu in WSL, install it, which also starts it. On a Mac, open **System Settings**, then **General**, then **Sharing**, and turn on **Remote Login**.

   ```bash
   sudo apt install openssh-server
   ```

2. **Put your key on it**, as in *SSH Keys and ~/.ssh/config*. If `ls ~/.ssh` shows no `id_ed25519`, make one first with `ssh-keygen -t ed25519`. Then hand its padlock to your own computer:

   ```bash
   ssh-copy-id "$(whoami)@localhost"
   ```

3. **Give `bookclub` its name for real.** Open `~/.ssh/config` in nano (it's a new, empty file if you don't have one yet), paste the `Host bookclub` block from your `ssh-config` at the end, and save. Then try it: `ssh bookclub ls` should list your home folder without asking for your computer's password. It may ask for your key's passphrase.
4. **Deploy.** Rehearse first, then deploy, then look at what's live:

   ```bash
   ./deploy.sh -n
   ./deploy.sh
   ls ~/bookclub-site
   ```

   The four pages are now in `~/bookclub-site`, copied there over SSH, as they would be on a server across the country. Delete a page from `site/`, deploy again, and watch it go from the live copy too.
5. **Tidy up.** Turn the server off, and take the `Host bookclub` block out of `~/.ssh/config` if you won't use it again. On a Mac, turn **Remote Login** off in the same place.

   ```bash
   sudo apt remove openssh-server
   rm -r ~/bookclub-site
   ```

On a real server, only the `Host` block changes: its `HostName`, its `User`, and the key you put there. `deploy.sh` stays the same.

## Submit your exercise

The exercise is how you finish this lesson. When your work is done, put it on GitHub in a repo of your own, then submit that repo's address here.

1. On GitHub, click **+**, then **New repository**. Name it (for example `exercise-bash-deploy-over-ssh`), choose **Public**, and click **Create repository**. Leave out the README: your folder already has one.
2. In a terminal in your exercise folder (Ubuntu in WSL on Windows), send your work to your new repo. If you downloaded the ZIP:

   ```bash
   git init -b main
   git add .
   git commit -m "My solution"
   git remote add origin https://github.com/your-name/exercise-bash-deploy-over-ssh.git
   git push -u origin main
   ```

   If you cloned the starter instead, point your copy at your own repo, then commit and push:

   ```bash
   git remote set-url origin https://github.com/your-name/exercise-bash-deploy-over-ssh.git
   git add .
   git commit -m "My solution"
   git push -u origin main
   ```

3. Paste your repo's address, such as `https://github.com/your-name/exercise-bash-deploy-over-ssh`, into **Submit your exercise** on this page. The site checks that the repo is public and holds your commits, and then the lesson is complete.

Changed something after you submitted? Push it, and submit again: your latest submission is the one that counts.

## So what

A deploy script is a checklist that can't skip a step: the same checks, the same copy and the same look at the result, every time, whether you run it or a scheduled job does.

## Key terms

- [ssh](https://www.ask-codey.com/glossary/ssh)
- [rsync](https://www.ask-codey.com/glossary/rsync)
- [SSH Config File](https://www.ask-codey.com/glossary/ssh-config-file)
- [Deploy Script](https://www.ask-codey.com/glossary/deploy-script)
- [Dry run](https://www.ask-codey.com/glossary/dry-run)

## Related

- [SSH Keys and ~/.ssh/config](https://www.ask-codey.com/lessons/bash-ssh-keys)
- [Copying Between Computers: scp and rsync](https://www.ask-codey.com/lessons/bash-copying-between-computers)
- [Running Commands on Another Computer](https://www.ask-codey.com/lessons/bash-remote-commands)
- [Backups That Work: rsync](https://www.ask-codey.com/lessons/bash-rsync)

Source: https://www.ask-codey.com/lessons/exercise-bash-deploy-over-ssh
