⌂ Self-hosting

Updating self-hosted services without surprises: a method

How to update the services you host at home without breaking them: pinned versions, release notes, a backup before every jump and tools that notify you.

A self-hosted service that is never updated piles up vulnerabilities; one updated blindly will eventually break. Between these two extremes there is a simple method that takes a few minutes a week and reduces the nasty Saturday-morning surprises to almost zero. I describe it here as I apply it, with the tools most common in the container world.

Why «latest» is a bad idea

Many example compose files use the latest tag. It looks convenient, but it means that every time you pull the image you may get a version very different from yesterday's, including incompatible changes. Worse, if something breaks, you cannot be sure which version you started from.

The solution is to pin the version explicitly:

services:
  app:
    image: example/app:2.4.1

That way an update becomes a conscious decision: you change the number, read what changes and apply it. Some projects also offer intermediate tags (for example 2.4 or 2) that receive only fixes or only compatible versions; they can be a good compromise, as long as you know what the project promises.

Reading the release notes

Before changing version, I read the release notes for every intermediate version, not just the latest. The information I look for is always the same:

  • Breaking changes: renamed variables, moved folders, removed options.
  • Database migrations: if the update changes the schema, going back to the previous version may not be possible.
  • Mandatory steps: some projects do not allow you to skip major versions. Nextcloud, for example, must be upgraded one major version at a time.
  • Dependencies: sometimes the required version of the database or of a linked service changes.

If the notes mention manual steps, I write them down and carry them out in order. If they are unclear, I wait a few days: reports from other users often clarify any problems.

A backup before every jump

An update that changes the database is the one moment when a recent backup becomes truly indispensable. The procedure I follow:

  1. Stop the service, so the database is not writing.
  2. Copy the data folder or export a database dump.
  3. Note the current image version.
  4. Change the version in the compose file and restart.
  5. Check the logs and try the main features.

If something goes wrong, I roll back by restoring the data and the noted version. That is why it pays to stop the service before copying: going back to the old version with a database already migrated by the new one usually does not work.

Databases have their own rules

The application and its database should be treated differently. Updating the application is frequent; upgrading the major version of PostgreSQL, on the other hand, requires exporting and importing the data or a dedicated migration tool, because the on-disk files are not compatible between major versions. For this reason, when a project allows it, I keep the database on the version stated in the documentation and upgrade it only when the project requires it.

Get notified instead of checking by hand

Checking dozens of projects by hand is tedious, and you end up giving up. It is better to rely on tools that tell you when a new version is out:

Approach What it does When to use it
Notification (e.g. Diun) Alerts you when an image has a new version Important services, updated by hand
Automatic update Pulls and restarts on its own Non-critical services without a database
Release subscription Alerts from the project repository Young projects or those with many changes
Automated proposals (e.g. Renovate) Proposes changes to compose files in a repository Anyone keeping configuration under Git

Personally, I accept automatic updates only for services without valuable data; for everything else I prefer a notification and a few minutes of reading.

A sustainable rhythm

The method works if it becomes a habit. A realistic rhythm for a household:

  • Every week: updates to the host operating system and security fixes.
  • Every two weeks or monthly: review notifications and update application services.
  • Immediately: when a project reports a serious vulnerability, especially if the service is reachable from outside.

Keeping a small log (date, service, starting and target version, any problems) helps you reconstruct what happened if a fault appears weeks later.

In short

  • Pinning versions turns every update into a conscious choice.
  • Release notes must be read for all intermediate versions.
  • Before an update that touches the database: service stopped and a backup taken.
  • Major database versions follow their own migration rules.
  • Notifications and a regular rhythm avoid both neglect and blind updates.
MM
Marco Marzaduri

Marco Marzaduri works in cybersecurity, digital forensics and web development. A computer science graduate, he combines rigorous technical analysis with attention to the people who use technology.

Read also