Side reading

Tags move, digests do not #

A tag is a label the publisher can point anywhere. :v3 today and :v3 next month may be different software. :latest certainly is.

A digest is a hash of the image content. @sha256:bcf633... is one specific image for ever.

$ sudo docker image inspect ghcr.io/immich-app/immich-server:v3 --format '{{index .RepoDigests 0}}'

Pin both, so the file stays readable and the behaviour stays fixed:

image: ghcr.io/immich-app/immich-server:v3@sha256:<the digest you looked up>

The tag is documentation for humans. The digest is what Docker uses.

Why bother #

Reproducibility. The same file produces the same system next month, and on a rebuilt machine.

Deliberate upgrades. Changing software becomes a commit you wrote, with a date and a message, rather than something that happened because you ran pull.

Supply chain. A publisher whose account is compromised can move a tag. They cannot change what a digest refers to.

Pinning without reviewing is worse than not pinning

A pinned digest never gets security fixes. If you pin and then never look again, you are running old software on purpose.

Pinning only makes sense with a habit attached: a calendar entry, monthly, to review and bump. If you know you will not do that, tracking a minor version tag is the more honest choice.

Reading a release note #

Before bumping anything, read from your current version to the target. The questions worth asking, in order:

Does it migrate the database? The most important one. Look for "migration", "schema", "will be upgraded automatically". Then the follow-up: is it reversible? Usually not. Once the schema is upgraded, the old version cannot read it, and rolling back means restoring from backup.

Is it a major version? Semantic versioning is a convention, not a promise, but a major bump is a signal to read more carefully.

Are there breaking configuration changes? Renamed environment variables, removed options, a changed default. These usually appear as a container that starts and then exits.

Does it need a specific upgrade path? Some projects require passing through an intermediate version. Skipping it produces a broken database.

What is the security content? A fix for something you are exposed to moves the upgrade up your list.

The upgrade procedure #

# 1. back up first, and know you can restore
$ sudo /usr/local/sbin/homeserver-backup
$ sudo /usr/local/sbin/homeserver-restic snapshots

# 2. write down what you are on
$ sudo docker compose images

# 3. change the digest in the file, commit the change
# 4. pull and recreate
$ sudo docker compose pull
$ sudo docker compose up -d

# 5. watch it come up
$ sudo docker compose logs -f

Step 1 is not optional for anything that touches a database. Step 3 being a commit is what gives you the record of when a change happened, which is the first question you will ask when something is odd a week later.

Do one application at a time. Two at once means two suspects.

Rolling back #

Change the digest back and up -d, if nothing migrated. That is the whole reason for asking the first question.

If the schema moved, rolling back means restoring the database from the backup you took in step 1, and losing anything written since. This is why the backup comes first, and why trying an upgrade to see what happens is expensive for a database.

Automating the boring part #

Tools like Renovate or Diun watch for new versions. Configure them to propose a change: a pull request, or a notification.

This book uses those tools to propose changes, not deploy them. The review, tested backup and deliberate Compose run remain separate steps.

Settings

Your values

The book is written with placeholder names so it makes sense to everybody. Put your own in and every chapter, every command and every copy-paste prompt updates to match.

Nothing here is sent anywhere. It is saved in this browser, so it comes back next time. A different browser or a private window gets the placeholders again.

Live preview

$ ssh admin@192.168.1.20
$ sudo ufw allow from 192.168.1.0/24 to any port 22 proto tcp
$ sudo hostnamectl set-hostname homeserver
$ sudo timedatectl set-timezone Europe/Paris

Real commands from chapters 2, 3 and 4. They change as you type.

The account you log in as. Not root, and not necessarily the same name you use on your laptop.

Introduced in Chapter 2, Meet your server

The book's placeholder is admin

What the machine calls itself. You choose it, and it shows up in your shell prompt and your logs.

Introduced in Chapter 3, A safe front door

The book's placeholder is homeserver

The IP address your server has on your home network, from ip -brief addr.

Introduced in Chapter 2, Meet your server

The book's placeholder is 192.168.1.20

The address range and prefix shown by ip route or ip -brief addr, written in CIDR form. Copy the real prefix; do not guess /24.

Introduced in Chapter 2, Meet your server

The book's placeholder is 192.168.1.0/24

The address traffic goes to on its way out of your house, from ip route.

Introduced in Chapter 2, Meet your server

The book's placeholder is 192.168.1.1

In Region/City form, or Etc/UTC if you would rather read logs in UTC.

Introduced in Chapter 3, A safe front door

The book's placeholder is Europe/Paris

A registered name you control. Chapter 8 uses it for the LAN route; chapter 12 uses a separate private Tailscale name remotely.

Introduced in Chapter 8, One door, many rooms

The book's placeholder is example.com

The email identity allowed to administer the tagged server in your Tailscale policy.

Introduced in Chapter 10, Your own private network

The book's placeholder is you@example.com

The mailbox that should receive actionable home-server alerts.

Introduced in Chapter 14, Knowing it is alive

The book's placeholder is alerts@example.com

Once you save, the prose and the commands read with your names, the copy buttons copy your values, and the copy-paste prompts describe your machine accurately. That last one matters: an assistant told your network is 192.168.1.0/24 when it is not will send you chasing the wrong thing.

Anything you leave empty keeps the book's placeholder.