Wallos mit Docker Compose selbst hosten: Abo- und Vertragskosten im Blick
Wallos verfolgt wiederkehrende Abos und Vertragskosten in einem einzigen Container mit SQLite. Die Anleitung zeigt Installation mit Docker Compose (getestet 5.10.0), Rechte im Bind-Mount, Reverse Proxy, Backup, Wiederherstellung und Updates.
Werbung Mit * markierte Links sind Werbelinks: Bei einem Kauf erhalten wir eine Provision, der Preis bleibt gleich. Als Amazon-Partner verdiene ich an qualifizierten Verkäufen. Mehr dazu

Softwarelizenzen, Domains, Hosting, Mobilfunk, Cloud-Speicher: Jede einzelne Position ist klein, die Summe nicht. Wallos ist ein quelloffener, selbst hostbarer Tracker für wiederkehrende Abos und Vertragskosten. Die Anwendung läuft als einzelner Container mit SQLite-Datenbank, ohne separaten Datenbankdienst. Diese Anleitung zeigt Installation, Erstkonfiguration, Rechte im Bind-Mount, Absicherung, Backup, Updates und typische Fehler.
Voraussetzungen
- Docker Engine mit Compose-Plugin (
docker compose). Das Image basiert auf Alpine Linux und läuft auf Linux-Servern, NAS-Systemen mit Container-Unterstützung und Docker Desktop. Der Test lief auf einem Linux-Host. - CPU und RAM: 1 vCPU und 1 GB RAM reichen für eine PHP-Anwendung mit SQLite. Eine offizielle Mindestangabe nennt das Projekt nicht.
- Speicher: wenige hundert MB inklusive Image. Im Test war die frische
wallos.db200704 Byte groß, ein Offline-Backup rund 7 bis 8 KB. - Ein freier TCP-Port auf dem Host, das offizielle Beispiel nutzt 8282.
- Für den Zugriff von außen: ein Reverse Proxy mit TLS-Zertifikat.
| Eckdaten | Wert |
|---|---|
| Image | bellamy/wallos (Docker Hub), getestet mit 5.10.0 |
| Port | 80 im Container, auf dem Host 8282 |
| Volumes | ./db nach /var/www/html/db, ./logos nach /var/www/html/images/uploads/logos |
| Env | TZ, optional OIDC_* |
| Laufzeit im Image | PHP 8.3.35 (Stand 5.10.0), Webserver-Benutzer www-data mit UID 82 |
Wallos verwaltet laut README Abos mit Fälligkeiten, Kategorien, mehrere Währungen (Umrechnung über die Fixer-API), Statistiken, Benachrichtigungen per E-Mail, Discord, Pushover, Telegram, Gotify und Webhook sowie OIDC-Anmeldung. Es ist kein Buchhaltungssystem, hat keine Bankanbindung und kein Mandantenmodell. Für mehrere Gesellschaften brauchen Sie mehrere Instanzen, und Kündigungsfristen über Fälligkeiten sind kein rechtssicheres Fristenmanagement.
Schritt 1: compose.yaml anlegen
# Verzeichnis fuer den Stack anlegen und hineinwechseln
mkdir -p /opt/wallos
cd /opt/wallos
Unter /opt braucht ein normaler Benutzer dafür in der Regel sudo mkdir -p /opt/wallos und anschließend sudo chown "$USER": /opt/wallos. Das Projekt liefert in der README diese Vorlage:
services:
wallos:
container_name: wallos
image: bellamy/wallos:latest
ports:
- "8282:80/tcp"
environment:
TZ: 'America/Toronto'
volumes:
- './db:/var/www/html/db'
- './logos:/var/www/html/images/uploads/logos'
restart: unless-stopped
Für eine deutsche Installation passen Sie die Zeitzone an. Die Praxisvariante bindet den Port zusätzlich an die Loopback-Adresse, damit die Anwendung nicht ungeschützt aus dem Netz erreichbar ist, solange kein Reverse Proxy davor steht. Speichern Sie sie als /opt/wallos/compose.yaml:
services:
wallos:
container_name: wallos
image: bellamy/wallos:5.10.0
# Nur lokal veroeffentlichen, der Reverse Proxy nimmt den externen Verkehr an
ports:
- "127.0.0.1:8282:80/tcp"
environment:
TZ: 'Europe/Berlin'
volumes:
- './db:/var/www/html/db'
- './logos:/var/www/html/images/uploads/logos'
restart: unless-stopped
- Image-Tag:
latestist nicht reproduzierbar. Die Praxisvariante schreibt deshalb den versionierten Tag5.10.0fest, dann ist ein Update eine bewusste Entscheidung und nicht die Nebenwirkung einesdocker compose pull. - ports: Der Container lauscht immer auf Port 80, Sie ändern nur die linke Seite.
- TZ: Fälligkeiten, die Berechnung der nächsten Zahlung und die Hintergrundaufgaben hängen an der Zeitzone.
- ./db: SQLite-Datenbank. Ohne diesen Mount sind alle Daten nach dem Entfernen des Containers verloren.
docker compose config -q gibt nichts aus, und ss -tlnp | grep 8282 bleibt leer, der Port ist also frei.
Schritt 2: Stack starten und prüfen
# Image ziehen und Stack im Hintergrund starten
docker compose up -d
# Laufzustand und Healthcheck des Containers anzeigen
docker compose ps
Das Image bringt einen eigenen Healthcheck mit. Im Test meldete der Container nach 6 bis 19 Sekunden Up (healthy). Bleibt die Spalte länger auf starting, sehen Sie in die Logs:
# Laufende Logausgabe des Containers verfolgen
docker compose logs -f wallos
Die Erreichbarkeit prüfen Sie auf der Kommandozeile.:
# Startseite abfragen, nur den HTTP-Status ausgeben
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8282/
# Registrierungsseite fuer den ersten Benutzer pruefen
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8282/registration.php
# PHP-Version im laufenden Container auslesen
docker compose exec wallos php -v
docker compose ps zeigt Up (healthy). Die Startseite antwortet mit 302 und leitet nicht angemeldete Besucher auf /login.php um, das ist der Erfolgsfall. registration.php liefert 200, php -v meldet bei 5.10.0 PHP 8.3.35.
Schritt 3: Erstes Konto und Grundeinstellungen
Die Ersteinrichtung läuft laut Dokumentation vollständig über die Weboberfläche. Rufen Sie die Instanz im Browser auf, lokal per SSH-Tunnel (ssh -L 8282:127.0.0.1:8282 server) oder später über den Reverse Proxy. Der erste Aufruf führt zur Registrierung, vergeben Sie ein starkes, eigenständiges Passwort.
- Haushaltsmitglieder: In den Einstellungen pflegen Sie weitere Mitglieder, denen Abos zugeordnet werden, etwa Abteilungen.
- Kategorien: Die mitgelieferten Kategorien sind ein Vorschlag. Für betriebliche Nutzung bilden Sie eigene ab, zum Beispiel Softwarelizenzen, Hosting, Telekommunikation, Versicherungen.
- Währungsumrechnung: Hinterlegen Sie einen kostenlosen API-Schlüssel von Fixer. Laut README aktualisieren Sie die Kurse danach, indem Sie die Hauptwährung wechseln und wieder zurückstellen.
OIDC aktivieren Sie auf der Admin-Seite oder deklarativ über Umgebungsvariablen. Ein gesetztes OIDC_* überschreibt laut README den Datenbankwert zur Laufzeit. Ist OIDC_ISSUER gesetzt, holt Wallos die Endpunkte per Discovery über /.well-known/openid-configuration:
# .env neben der compose.yaml, Platzhalter durch eigene Werte ersetzen
OIDC_ISSUER=https://idp.example.com/realms/EXAMPLE
OIDC_CLIENT_ID=PLATZHALTER_CLIENT_ID
OIDC_CLIENT_SECRET=PLATZHALTER_CLIENT_SECRET
Sichern Sie die .env mit chmod 600 und binden Sie sie in der compose.yaml mit env_file: .env am Dienst wallos ein. Erst dann gelangen die Werte in den Container.
Beim Anlegen eines Abos ist „Nächste Zahlung“ ein Pflichtfeld. Das Zauberstab-Symbol daneben berechnet den Termin aus Startdatum und Zyklus, im Test aus 01.03.2026 und jährlich der 01.03.2027.


Nach der Anmeldung zeigt das Dashboard die leere Abo-Liste. Ein erstes Test-Abo mit Fälligkeit erscheint mit korrektem nächsten Zahlungstermin.
Schritt 4: Rechte im Bind-Mount verstehen
Nach dem ersten Start lagen im Test in ./db zwei Dateien: wallos.db (200704 Byte) und setup_token.db (64 Byte). Beide gehören der UID 82, also www-data im Alpine-Image, nicht dem Host-Benutzer. Das hat zwei Folgen: Ein rm -rf als normaler Benutzer scheitert mit Permission denied, und Backup-Werkzeuge ohne Root-Rechte können die Datenbank unter Umständen nicht lesen.
# Eigentuemer und Rechte im Datenbankverzeichnis anzeigen
ls -ln /opt/wallos/db
Statt die Rechte auf dem Host dauerhaft aufzuweichen, arbeiten Sie bei Bedarf über einen Wegwerfcontainer:
# Dateien im Bind-Mount ueber einen Wegwerfcontainer bearbeiten
docker run --rm -v /opt/wallos:/host alpine sh -c 'ls -ln /host/db'
ls -ln /opt/wallos/db zeigt bei beiden Dateien die Eigentümer-ID 82.
Schritt 5: Reverse Proxy mit TLS
Wallos spricht im Container reines HTTP und bringt kein TLS mit. Anmeldedaten gingen unverschlüsselt über die Leitung. Betreiben Sie Wallos deshalb nur im internen Netz, über VPN oder hinter einem Reverse Proxy mit gültigem Zertifikat:
- Port an
127.0.0.1binden wie in Schritt 1, damit niemand die Anwendung am Proxy vorbei erreicht. - Einen Proxy mit automatischem Zertifikat davorsetzen, etwa Caddy oder den Nginx Proxy Manager, Ziel
http://127.0.0.1:8282. - HTTP auf HTTPS umleiten,
X-Forwarded-ForundX-Forwarded-Protodurchreichen. /registration.phpnach Anlage des ersten Kontos nicht offen ins Internet stellen, etwa über eine Zugriffsbeschränkung im Proxy.
Die Einrichtung des Nginx Proxy Manager mit Zertifikat zeigt die Anleitung zu Collabora Online mit Nginx Proxy Manager.
curl -sI https://abos.example.de/ liefert 302 mit Location auf /login.php und gültigem Zertifikat. Von einem anderen Rechner ist http://SERVER-IP:8282 nicht erreichbar.
Schritt 6: Backup und Wiederherstellung
Die Anwendung besteht aus einer SQLite-Datei und einem Logo-Verzeichnis. Der empfohlene Weg ist das Offline-Backup. Im Test wurde der Container gestoppt, db und logos per tar gesichert (rund 7 bis 8 KB) und der Stack wieder gestartet. Danach antwortete /login.php wieder normal. Führen Sie tar als root aus, damit die UID-82-Dateien lesbar sind und /var/backups beschreibbar ist:
# Container anhalten, damit keine Schreibzugriffe laufen
docker compose stop
# Datenbank und Logos in ein Archiv sichern
sudo tar czf /var/backups/wallos-$(date +%F).tar.gz -C /opt/wallos db logos
# Container wieder starten
docker compose start
Im laufenden Betrieb funktionierte im Test sqlite3 .backup über einen Wegwerfcontainer (Ergebnis 200704 Byte, Exit 0). Das Wallos-Image bringt sqlite3 zwar selbst mit (im Test 3.53.4), der Wegwerfcontainer schreibt aber nichts in den laufenden Anwendungscontainer. Ein einfaches cp der laufenden Datenbank ist kein Backup:
# Konsistente Kopie der laufenden Datenbank ueber einen Wegwerfcontainer
docker run --rm -v /opt/wallos/db:/db alpine sh -c 'apk add --no-cache sqlite >/dev/null && sqlite3 /db/wallos.db ".backup /db/wallos-backup.db"'
Die Wiederherstellung kehrt das Offline-Backup um: erst stoppen, dann austauschen, dann starten. tar als root behält beim Entpacken die ursprünglichen Eigentümer, sonst kann der Container nicht mehr in die Datenbank schreiben:
# Container anhalten
docker compose stop
# Alte Daten beiseite legen, statt sie sofort zu loeschen
mv /opt/wallos/db /opt/wallos/db.alt
mv /opt/wallos/logos /opt/wallos/logos.alt
# Sicherung entpacken
sudo tar xzf /var/backups/wallos-2026-10-16.tar.gz -C /opt/wallos
# Container starten und Erreichbarkeit pruefen
docker compose start
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8282/
Spielen Sie die Wiederherstellung einmal bewusst auf einem Testsystem durch, bevor Sie sich im Ernstfall darauf verlassen. Automatisiert und versioniert ablegen lassen sich die Archive etwa mit borgmatic.
tar tzf auf das Archiv listet db/wallos.db. Nach dem Start antwortet die Instanz mit 302, und nach der Anmeldung sind alle Abos wieder da.
Schritt 7: Updates
Ziehen Sie vor jedem Update ein Offline-Backup. Wallos führt Datenbankmigrationen beim Start aus. Ein älteres Image kann mit dem migrierten Schema in der Regel nicht arbeiten, ein Rollback heißt deshalb: altes Image plus Sicherung von vor dem Update. Lesen Sie die Release Notes, vor allem bei Sprüngen über mehrere Versionen. Mit festem Tag ändern Sie zuerst die Versionsnummer in der compose.yaml:
# Neues Image herunterladen
docker compose pull
# Container mit dem neuen Image neu erzeugen
docker compose up -d
# Status und Healthcheck kontrollieren
docker compose ps
Die Hintergrundaufgaben, die bei Installation ohne Container als eigene Cronjobs nötig wären (laut README unter anderem updatenextpayment.php, updateexchange.php, sendnotifications.php), laufen im Docker-Image mit.
docker compose ps zeigt wieder healthy, die Startseite antwortet mit 302, und nach der Anmeldung sind alle Abos vorhanden.
Update von 5.8.3 auf 5.10.0
Im Test genügte der neue Tag in der compose.yaml. Beim Start liefen die Migrationen bis 000061.php durch, der Container war nach wenigen Sekunden healthy. Die Anmeldung mit dem bestehenden Konto gelang, ein vorher angelegtes Abo war mit Kategorie und Zahler erhalten, ein falsches Passwort wurde abgewiesen, und nicht angemeldete Aufrufe leiteten weiter auf /login.php. PHP blieb bei 8.3.35, die Dateien in ./db gehören weiterhin UID 82. Ein Offline-Backup aus 5.8.3 ließ sich unter 5.10.0 zurückspielen, die Anmeldeseite antwortete danach mit 200.
Neu sind laut Release Notes mehrere Kategorien und mehrere Zahler je Abo (5.9.0) sowie Sammelaktionen auf der Abo-Seite und Preis und Verlängerungsart im Kalender (5.10.0). Die Migration überträgt die bisherige Kategorie und den bisherigen Zahler in neue Zuordnungstabellen (subscription_categories, subscription_payers). Wer Datenbank oder API mit eigenen Skripten ausliest, prüft diese danach. Außerdem antwortet die API seit 5.10.0 bei abgelehnten Anfragen mit einem HTTP-Fehlerstatus statt mit 200: Skripte, die nur auf den Statuscode achten, verhalten sich dadurch anders.
Typische Fehler
| Fehlerbild | Ursache | Lösung |
|---|---|---|
Startseite liefert 302 statt 200 | Kein Fehler: Umleitung auf /login.php, im Test bestätigt | curl -sL nutzen oder /login.php direkt abfragen |
| Port bereits zugewiesen | Anderer Dienst auf 8282, im Test aufgetreten | ss -tlnp | grep 8282, freien Host-Port links im ports-Eintrag wählen |
Permission denied beim Löschen des Stack-Verzeichnisses | Dateien gehören UID 82, im Test bestätigt | Als root oder per Wegwerfcontainer löschen |
Healthcheck bleibt auf starting | Laut README bei Docker älter als Version 25 möglich | Docker aktualisieren oder healthcheck: test: ["NONE"] am Dienst setzen |
| Falsche Fälligkeitstermine | TZ steht noch auf America/Toronto | TZ: 'Europe/Berlin': Container mit docker compose up -d neu erstellen |
| Wechselkurse aktualisieren sich nicht | Kein gültiger Fixer-API-Schlüssel | Schlüssel eintragen, Hauptwährung wechseln und zurückstellen |
| Altes Image startet nach Update nicht sauber | Datenbankmigration bereits gelaufen | Backup von vor dem Update einspielen |
Häufige Fragen
Eignet sich Wallos für die Buchhaltung?
Nein. Wallos macht wiederkehrende Kosten sichtbar, ersetzt aber weder Buchhaltung noch GoBD-konforme Belegablage. Für die IT-Abteilung, die Softwareabos und Domains im Blick behalten will, ist es gut geeignet.
Wie entfernen Sie Wallos vollständig?
docker compose down entfernt den Container, die Daten in den Bind-Mounts bleiben. Danach docker image rm bellamy/wallos:5.10.0 und, nur mit geprüftem Backup, die Datenverzeichnisse per Wegwerfcontainer: docker run --rm -v /opt:/host alpine sh -c 'rm -rf /host/wallos/db /host/wallos/logos'.
Geht es auch ohne Docker Compose?
Ja, mit docker run und denselben Mounts. Ohne Container brauchen Sie laut README NGINX oder Apache, PHP 8.3 mit mehreren Modulen und eigene Cronjobs. Die Compose-Variante hält die Konfiguration versionierbar in einer Datei.
Fazit
Wallos ist in wenigen Minuten betriebsbereit, braucht kaum Ressourcen und macht verstreute Abo-Kosten auf einen Blick sichtbar. Für den Betrieb zählen vier Punkte: Version festschreiben, Port nur lokal binden und per TLS-Proxy veröffentlichen, die UID 82 im Bind-Mount bei Backups berücksichtigen und vor jedem Update offline sichern.
Weiterführende Anleitungen und Quellen
- Docker Compose absichern
- Docker und Docker Compose auf Linux installieren
- Collabora Online mit Docker, Nextcloud und Nginx Proxy Manager
- borgmatic im Docker-Container für automatische Server-Backups
- 2FAuth mit Docker Compose selbst hosten
- GitHub: ellite/Wallos mit README
- Wallos Releases
- Docker Hub: bellamy/wallos


