Runbook

One job per box. Find the thing you are trying to do and do it.

Update to a new version

docker compose pull
docker compose down
docker compose up -d

Your data is in the directory you mounted, not in the container, so it comes through untouched. A plain up -d will not fetch a newer latest.

Take a backup

Settings, then Download a backup. One JSON file, keep it somewhere that is not the same machine.

Copying the whole data directory works as well and catches the cached artwork too. Stop the container first so the database is not written to while you copy:

docker compose stop
sudo tar czf nextup-backup.tar.gz -C /opt nextup
docker compose start

Move to another machine

Install Nextup on the new machine first, then one of these.

  • From the JSON file. Settings, then restore the backup. If you used the button that leaves the secrets out, type the TMDB key and the mail password in again afterwards.
  • From the whole directory. Stop the container on both sides, copy /opt/nextup across keeping the ownership, and start it. It carries on exactly where it was, keys and all.

Change the password

Settings, then the account section. The same page changes the username. It takes effect at once and does not sign you out.

Forgotten the password

There is no reset email, because there is no email address on the account. Clear the one account out of the database and the app makes it again as admin and nextup when it next starts. Nothing else is lost.

docker compose stop
sudo sqlite3 /opt/nextup/nextup.db "DELETE FROM admin_user;"
docker compose start

Then sign in with those and change them straight away.

Put it behind Tailscale Serve or a proxy

Bind the container to this machine only, so the proxy is the one thing listening on the outside:

ports:
  - "127.0.0.1:4090:8080"

Leaving it on every interface while a proxy wants the same port means whichever starts first wins, and the symptom is address already in use on a restart that worked yesterday.

Turn on HTTPS only

Once every way in is HTTPS, add NEXTUP_HTTPS_ONLY=1 to the environment and recreate the container. The login cookie is then marked Secure.

Turn it off again if you ever need to reach the container directly on plain HTTP, or the sign-in page will take your password and send you back to itself.

Get the clock right

The morning email and the meaning of "today" both read the clock. The compose file mounts /etc/localtime read only, so the container follows the machine. If you want a different timezone, set TZ instead.

The morning email is not arriving

  • Check the switch is on and the time is one that has already passed today.
  • Check the security setting matches the port: STARTTLS on 587, SSL on 465, neither on 25.
  • Nothing is sent on a day when nothing became watchable. That is not a fault.
  • The watchable-after setting holds things back, so something that aired late last night may be waiting until later today.

The container log says what the mail server replied:

docker compose logs --tail 50 nextup

TMDB will not take the key

The key is checked against TMDB before it is saved, so a refusal means TMDB itself said no. Make sure you copied the API Key for v3 auth and not the read access token, which is much longer. A new key can take a few minutes to start working.

Posters are missing

Artwork is fetched from TMDB once and then cached in images/ inside your data directory. If none of it appears, the server either cannot reach TMDB or cannot write to that directory. The directory has to belong to user 1000:

sudo chown -R 1000:1000 /opt/nextup

Changes do not save, and nothing says why

Almost always the same thing: the data directory does not belong to user 1000, so the writes fail quietly. The command above fixes it. Restart afterwards.

The container will not start

docker compose logs --tail 50 nextup
docker compose ps

address already in use means something else holds the port; see the proxy note above. A permissions error on /data means the ownership again. If it starts and then stops, the log says why on the last few lines.

Check it is healthy

The image carries its own health check, so Docker knows whether it is really answering:

docker ps --filter name=nextup --format "{{.Status}}"

It should say healthy rather than only Up.

Start again from nothing

Take a backup first if there is anything you want to keep. Then stop it, delete the data directory and bring it back up. It builds a fresh database and the default account.

docker compose down
sudo rm -rf /opt/nextup
sudo mkdir -p /opt/nextup
sudo chown -R 1000:1000 /opt/nextup
docker compose up -d