Applications Azure

Maintainerr on Ubuntu 24.04 on Azure User Guide

| Product: Maintainerr on Ubuntu 24.04 LTS on Azure

Overview

Maintainerr is an open source, self-hosted, rules-based media-collection manager. It connects to Plex together with Overseerr or Jellyseerr and the *arr apps (Sonarr and Radarr), then applies rules you define to keep your library tidy automatically: it finds unwatched, unrequested or otherwise stale media and can add it to a Plex collection for review or remove it, all on a schedule. Maintainerr has no built-in user authentication of its own because it is designed to run behind a reverse proxy, so the cloudimg image closes that gap: the application is built from source and runs as a single Node.js service bound to loopback 127.0.0.1:6246, and an nginx reverse proxy on port 80 adds a per-VM HTTP Basic Auth gate in front of it. The nginx Basic Auth credential (user admin, a unique password generated on first boot) is the gate, and an unauthenticated request is refused. Maintainerr's data - its SQLite database, configuration and logs - lives under /opt/data. Backed by 24/7 cloudimg support.

What is included:

  • Maintainerr 3.18.0 built from source and running as the maintainerr systemd service
  • The Maintainerr web UI and REST API on :80, fronted by nginx with the app bound to loopback only
  • Per-VM HTTP Basic Auth (user admin) protecting the UI and API, with a unique password generated on first boot
  • The Node.js service bound to 127.0.0.1:6246 - nothing is exposed to the network directly
  • A fresh per-instance database and internal API key created automatically on first boot
  • maintainerr.service + nginx.service as systemd units, enabled and active
  • An unauthenticated /healthz endpoint for Azure Load Balancer health probes
  • 24/7 cloudimg support

Prerequisites

An active Azure subscription, an SSH key pair, and a VNet + subnet in the target region. Standard_B2s (2 vCPU / 4 GiB RAM) is a comfortable starting point. NSG inbound: allow 22/tcp from your management network, 80/tcp for the UI and API, and 443/tcp if you add TLS. Maintainerr serves plain HTTP on port 80; for production use, terminate TLS in front of it with your own domain and restrict access to trusted IP ranges (see Maintenance).

Step 1 - Deploy from the Azure Marketplace

Sign in to the Azure Portal, choose Create a resource, search the Marketplace for Maintainerr by cloudimg, and select Create. On Basics pick your subscription, resource group, region and size; under Administrator account choose SSH public key and paste your key; under Inbound port rules allow SSH (22) and HTTP (80). Then Review + create -> Create.

Step 2 - Deploy from the Azure CLI

az vm create \
  --resource-group <your-rg> \
  --name maintainerr \
  --image <marketplace-image-urn> \
  --size Standard_B2s \
  --admin-username azureuser \
  --ssh-key-values ~/.ssh/id_ed25519.pub \
  --vnet-name <your-vnet> --subnet <your-subnet> \
  --public-ip-sku Standard

az vm open-port --resource-group <your-rg> --name maintainerr --port 80 --priority 1010

Step 3 - Connect to your VM

ssh azureuser@<vm-public-ip>

Step 4 - Confirm the services are running

systemctl is-active maintainerr.service nginx.service

Both report active. Maintainerr serves its web UI and REST API on the loopback address 127.0.0.1:6246; nginx fronts it on port 80 and adds the per-VM HTTP Basic Auth gate. Maintainerr's data lives under /opt/data.

The maintainerr and nginx services both reporting active on Ubuntu 24.04

Step 5 - Retrieve your web UI password

nginx protects the Maintainerr UI and API with HTTP Basic Auth. The username is admin and a unique password is generated on the first boot of your VM and written to a root-only file:

sudo cat /root/maintainerr-credentials.txt

This file contains MAINTAINERR_USERNAME, MAINTAINERR_PASSWORD and the MAINTAINERR_URL to open in a browser. The password is stored on disk only as a bcrypt hash in /etc/nginx/.maintainerr.htpasswd, so no plaintext password ships in the image. Store the password somewhere safe.

The per-VM Maintainerr credentials file showing the admin username, the generated password and the resolved MAINTAINERR_URL

Step 6 - Confirm the health endpoint

nginx serves an unauthenticated health endpoint for load balancers and probes:

curl -s http://localhost/healthz

It returns a small JSON document such as {"status":"ok","uptimeSeconds":65,"database":"ok","timestamp":"2026-07-19T09:33:37.855Z"}. This endpoint never requires authentication, so it is safe for an Azure Load Balancer health probe, and its database field confirms Maintainerr's SQLite store is answering.

Step 7 - Confirm authentication and the API

Because a password is set on first boot, an unauthenticated request to the UI returns HTTP 401, so nobody reaches Maintainerr without the password. The following reads the per-VM password from the credentials file and proves the round-trip - unauthenticated is rejected, a wrong password is rejected, the correct password authenticates, and an authenticated call to the readiness API returns 200:

PW=$(sudo grep '^MAINTAINERR_PASSWORD=' /root/maintainerr-credentials.txt | cut -d= -f2-)
echo "unauth  : $(curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1/)"
echo "wrongpw : $(curl -s -o /dev/null -w '%{http_code}' -u admin:wrong-pw http://127.0.0.1/)"
echo "authed  : $(curl -s -o /dev/null -w '%{http_code}' -u admin:$PW http://127.0.0.1/)"
echo "ready   : $(curl -s -o /dev/null -w '%{http_code}' -u admin:$PW http://127.0.0.1/api/health/ready)"

It prints unauth : 401, wrongpw : 401, authed : 200 and ready : 200. The whole UI and API are only reachable with the per-VM password because Maintainerr itself is bound to loopback and nginx is the only way in.

The HTTP Basic Auth round-trip returning 401 unauthenticated, 401 for a wrong password, 200 with the per-VM password, and 200 from the authenticated readiness API

Step 8 - Confirm the loopback binding and data directory

Maintainerr the application listens only on the loopback interface, and nginx is the only public listener. The following shows the Node.js service on 127.0.0.1:6246 and nginx on :80, then lists Maintainerr's data directory:

sudo ss -tlnp | grep -E ':6246|:80 '
sudo ls -la /opt/data

The application is bound to 127.0.0.1:6246 (never a public interface) and nginx listens on 0.0.0.0:80. Under /opt/data you will see maintainerr.sqlite (the SQLite database), a logs directory and an overlays directory. A fresh per-instance database and internal API key are created here on first boot, so no secret is shared between instances.

The Node.js service bound to loopback 127.0.0.1:6246 with nginx on port 80, and the Maintainerr data directory holding maintainerr.sqlite, logs and overlays

Step 9 - Sign in to the web UI

Browse to http://<vm-public-ip>/. Your browser prompts for a username and password: enter admin and the password from Step 5. Maintainerr opens on its first-run welcome, which prompts you to connect a media server to finish setup. The left sidebar already shows the full feature set - Overview, Rules, Collections, Calendar, Storage, Overlays and Settings - which unlocks once a media server is connected.

The Maintainerr web UI reached after signing in through the nginx HTTP Basic Auth gate, showing the first-run welcome prompting you to connect a media server

Step 10 - Choose your media server

Select Let's get started to open Settings -> General. Here you set the instance hostname and see the auto-generated internal API key, and under Media Server you choose the server Maintainerr will act on: Plex, Jellyfin or Emby. A fresh per-instance API key is generated on first boot, so no secret is shared between instances.

The Maintainerr General Settings page with the instance hostname and API key and the Media Server selection offering Plex, Jellyfin and Emby

Step 11 - Connect your media server

Pick your server type to open its connection form - for Jellyfin, enter the Jellyfin URL and an API key (created in Jellyfin under Dashboard -> API Keys) and use Test Connection to confirm it. Plex uses a sign-in flow instead. Once the connection is confirmed the setup guard clears and the rest of Maintainerr unlocks, so you can also connect your request manager (Overseerr or Jellyseerr) and the arr apps (Sonarr and Radarr) from their own Settings tabs.

The Maintainerr Jellyfin connection form asking for the Jellyfin URL, an API key and an admin user, with a Test Connection action

Step 12 - Build rules, review collections, and read the logs

With a media server connected, open the Rules page to create rule groups that match media by properties across your connected services - for example "added more than 180 days ago", "never watched", or "not in any active request" - and choose whether matching media is added to a managed Plex collection for review or removed. The Collections page then lists the managed collections Maintainerr builds, and the Logs page (available even during setup) records rule evaluations and connection diagnostics, which is the first place to look when confirming a rule behaves as intended.

The Maintainerr Log Settings and Logs page, available throughout setup, showing the log level and retention configuration and the record of rule evaluations

Maintenance

  • Password: the UI password is set on first boot and stored as a bcrypt entry in /etc/nginx/.maintainerr.htpasswd. To change it, run sudo htpasswd -B /etc/nginx/.maintainerr.htpasswd admin and then sudo systemctl reload nginx.
  • Restrict access: Maintainerr serves plain HTTP on port 80. For production, restrict access to trusted IP ranges in your Network Security Group, and front it with TLS (for example certbot with your own domain) terminating on :443.
  • Loopback binding: the application is bound to 127.0.0.1:6246 (via UI_HOSTNAME=127.0.0.1 in /etc/systemd/system/maintainerr.service), so nginx is the only path in. Keep it that way - do not change the bind address to a public interface.
  • Data and backups: all Maintainerr state (the SQLite database, configuration and logs) lives under /opt/data; back up that directory to protect your rules and connected-service configuration.
  • Logs: application logs are written under /opt/data/logs; service logs are available with journalctl -u maintainerr.
  • Updates: this image pins Maintainerr 3.18.0. To move to a newer release, follow the upstream upgrade notes; your /opt/data directory carries your configuration across upgrades.
  • Security patches: unattended-upgrades remains enabled so the OS continues to receive security updates automatically.

Support

cloudimg provides 24/7 expert support for this image. Contact support@cloudimg.co.uk.