Memos mit Docker installieren: Leichtgewichtiger Self-Hosted Memo-Hub für Schnellnotizen
Memos 0.31 mit Docker Compose selbst hosten: schlanker Notizdienst mit SQLite, festem Image-Tag, Zugriff über HTTPS-Reverse-Proxy, gesperrter Registrierung und konsistentem Backup.
Geprüft am 30.09.2026 · für Memos 0.31.0
WerbelinksMit * 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

Memos ist ein schlanker, selbst gehosteter Notizdienst für kurze Einträge, Code-Snippets und Tagesnotizen. Das Image ist rund 26 MB groß, läuft als einzelner Container und speichert Daten standardmäßig in einer SQLite-Datei; eine separate Datenbank ist nicht nötig. Diese Anleitung richtet Memos 0.31 mit Docker Compose auf einem Linux-Server ein, mit festem Image-Tag, Zugriff nur über einen Reverse Proxy mit HTTPS und konsistentem Backup.
Voraussetzungen
- Docker Engine 20.10+ und das Docker Compose Plugin v2 (
docker compose, nicht das altedocker-compose) müssen installiert sein, siehe Docker und Docker Compose auf Linux installieren. - Linux-Host, VM oder NAS mit Docker-Unterstützung (amd64, arm64 oder arm/v7, z. B. Raspberry Pi 4/5).
- Mindestens 256 MB freier RAM und 1 GB Speicherplatz für Daten.
- Für den Zugriff von anderen Geräten: Domain mit DNS-Eintrag und Reverse Proxy (Caddy, Traefik oder Nginx Proxy Manager), siehe Schritt 5.
Schritt 1: Projektordner anlegen
Alle Dateien für den Memos-Stack liegen in einem eigenen Verzeichnis:
mkdir -p /opt/memos
cd /opt/memosLegen Sie das Datenverzeichnis an. Der Container startet als root, setzt den Eigentümer von /var/opt/memos auf UID/GID 10001 und arbeitet danach als dieser Benutzer. Das chown vorab schadet nicht und vermeidet Probleme auf Dateisystemen, auf denen der Container keine Rechte ändern darf:
mkdir -p /opt/memos/memos-data
sudo chown -R 10001:10001 /opt/memos/memos-dataVerifizieren: ls -la /opt/memos/ zeigt memos-data mit Eigentümer 10001.
ls -la /opt/memos/
# Erwartete Ausgabe (gekürzt):
# drwxr-xr-x 2 10001 10001 ... memos-dataSchritt 2: .env-Datei anlegen
Für SQLite sind keine Pflichtvariablen nötig. MEMOS_INSTANCE_URL ist die öffentliche Adresse, die Memos für Links verwendet. Seit 0.31 ist sie unabhängig vom Zugriffsmodus; auf einer neuen Instanz setzt eine gesetzte URL den Modus beim ersten Start aber auf öffentlich (Schritt 4):
# /opt/memos/.env
# Oeffentliche URL der Instanz (absolute https-URL ohne Pfadparameter)
MEMOS_INSTANCE_URL=https://memos.example.com
# Log-Level: debug, info, warn, error (Standard: info)
MEMOS_LOG_LEVEL=info
# Datenbanktyp: sqlite (Standard), mysql, postgres
MEMOS_DRIVER=sqlite
Passen Sie MEMOS_INSTANCE_URL an Ihre Domain an oder kommentieren Sie die Zeile aus, solange kein Reverse Proxy existiert. Setzen Sie die Rechte: chmod 600 /opt/memos/.env.
Verifizieren: Die Datei enthält nur KEY=Wert-Zeilen und Kommentare:
cat /opt/memos/.env
# Ausgabe: drei KEY=Wert-Zeilen, keine Leerzeichen um das =Schritt 3: compose.yaml erstellen
Die folgende compose.yaml startet Memos mit SQLite, Healthcheck und festem Versions-Tag. Port 5230 ist nur auf 127.0.0.1 gebunden:
services:
memos:
image: neosmemo/memos:0.31.0
container_name: memos
restart: unless-stopped
ports:
- "127.0.0.1:5230:5230"
volumes:
- ./memos-data:/var/opt/memos
env_file:
- .env
environment:
MEMOS_PORT: "5230"
MEMOS_DRIVER: sqlite
healthcheck:
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:5230/healthz"]
interval: 30s
timeout: 10s
retries: 3
start_period: 20sZum Image-Tag: 0.31.0 ist fest und reproduzierbar; Updates erfolgen bewusst durch Ändern des Tags (Schritt 6). stable folgt automatisch der neuesten stabilen Version, latest und canary sind Entwicklungsstände und für den Produktivbetrieb ungeeignet.
Verifizieren: Syntax prüfen:
docker compose -f /opt/memos/compose.yaml config
# Keine Fehlermeldungen; gibt die geparste Konfiguration ausSchritt 4: Memos starten und Ersteinrichtung
Starten Sie den Stack aus dem Projektverzeichnis:
cd /opt/memos
docker compose up -dNach etwa 20 Sekunden (start_period des Healthchecks) meldet der Container healthy:
docker compose ps
# Erwartete Ausgabe:
# NAME IMAGE COMMAND SERVICE STATUS PORTS
# memos neosmemo/memos:0.31.0 ... memos Up X min (healthy) 127.0.0.1:5230->5230/tcpLogs auf Fehler prüfen:
docker compose logs memos
# Erwartete Ausgabe (Auszug):
# memos | Data directory: /var/opt/memos
# memos | Database driver: sqlite
# memos | Server running on port 5230Erreichbarkeit per curl testen:
curl -I http://localhost:5230
# HTTP/1.1 200 OK (oder 302 Found bei Weiterleitung auf /auth)Ersteinrichtung im Browser: Ohne Reverse Proxy erreichen Sie die Oberfläche per SSH-Tunnel: ssh -L 5230:127.0.0.1:5230 benutzer@server, dann http://localhost:5230. Der erste angelegte Account erhält Administratorrechte. Prüfen Sie danach in den Einstellungen unter Instanz zwei Punkte: den Zugriffsmodus (privat oder öffentlich lesbar) und ob neue Benutzer sich selbst registrieren dürfen. Für eine interne Notizablage wählen Sie privat und deaktivieren die Registrierung.
Verifizieren: docker compose ps zeigt (healthy), ss -tlnp | grep 5230 zeigt nur 127.0.0.1:5230. In einem privaten Browserfenster erscheint nach der Umstellung keine Registrierungsmöglichkeit mehr.
Schritt 5: HTTPS und Reverse Proxy (optional, empfohlen)
Memos bringt kein TLS mit. Für den Zugriff von anderen Geräten schalten Sie einen Reverse Proxy auf demselben Host vor, der die Header Host, X-Forwarded-Proto und X-Forwarded-For weiterreicht. Caddy holt Let's-Encrypt-Zertifikate automatisch, siehe Caddy als Reverse Proxy einrichten.
Bei mehreren Docker-Diensten eignet sich Traefik: Traefik als Docker-Reverse-Proxy mit automatischem HTTPS.
Minimales Caddy-Beispiel (Caddyfile-Auszug):
memos.example.com {
reverse_proxy localhost:5230
}Setzen Sie in der .env MEMOS_INSTANCE_URL=https://memos.example.com und erstellen Sie den Container neu:
cd /opt/memos
docker compose up -d --force-recreateVerifizieren: curl -I https://memos.example.com liefert HTTP/2 200; der Browser zeigt ein gültiges Zertifikat, und von Memos erzeugte Links beginnen mit https://memos.example.com.
Schritt 6: Updates und Backup
Memos führt Datenbankmigrationen beim Start automatisch aus; ein Downgrade danach ist nicht möglich, die Sicherung ist der einzige Rückweg. Sichern Sie deshalb vor jedem Update. Für eine konsistente SQLite-Sicherung stoppen Sie den Container kurz:
cd /opt/memos
sudo mkdir -p /backup/memos
docker compose stop memos
sudo tar -czf /backup/memos/memos-$(date +%Y%m%d).tar.gz -C /opt/memos memos-data .env compose.yaml
docker compose start memosmemos-data enthält die Datenbank memos_prod.db und den Ordner assets mit hochgeladenen Dateien. Für eine automatisierte Sicherung außer Haus eignet sich Restic mit automatischer Cron-Sicherung.
Lesen Sie vor dem Update die Release Notes der übersprungenen Versionen, besonders bei einem Wechsel der Minor-Version (0.x auf 0.y). Setzen Sie dann in der compose.yaml den neuen Tag, z. B. neosmemo/memos:0.31.1, und starten Sie neu:
docker compose pull
docker compose up -d
docker image prune -fVerifizieren: docker compose ps zeigt den neuen Tag und (healthy), docker compose logs memos enthält keine Migrationsfehler, und bisherige Memos sind vorhanden. tar -tzf auf die Sicherungsdatei listet memos-data/memos_prod.db.
Eckdaten auf einen Blick
| Eigenschaft | Wert |
|---|---|
| Offizielles Image | neosmemo/memos (Docker Hub) |
| Aktuelle stabile Version | 0.31.0 (19. September 2026) |
| Image-Größe | rund 26 MB (Go-Binary auf Alpine) |
| Architekturen | linux/amd64, linux/arm64, linux/arm/v7 |
| Web-UI / API-Port | 5230 |
| Daten-Volume im Container | /var/opt/memos |
| Standard-Datenbank | SQLite (memos_prod.db) – kein Begleitcontainer nötig |
| Alternativen DBs | MySQL 8+, PostgreSQL |
| Container-User (UID/GID) | 10001 / 10001 (Non-Root) |
| Lizenz | MIT |
Wichtige Umgebungsvariablen
| Variable | Standard | Beschreibung |
|---|---|---|
| MEMOS_PORT | 5230 (Image), sonst 8081 | Interner Listener-Port |
| MEMOS_ADDR | (leer = alle) | Bind-Adresse im Container |
| MEMOS_DATA | /var/opt/memos | Datenpfad im Container |
| MEMOS_DRIVER | sqlite | Datenbanktyp: sqlite, mysql, postgres |
| MEMOS_DSN | (leer) | Verbindungsstring (Pflicht bei MySQL/PG) |
| MEMOS_INSTANCE_URL | (leer) | Öffentliche URL für Links; unabhängig vom Zugriffsmodus |
| MEMOS_LOG_LEVEL | info | debug, info, warn, error |
| MEMOS_UID / MEMOS_GID | 10001 | Container-User-IDs |
Troubleshooting / Typische Fehler
- „permission denied“ beim Start: Memos arbeitet als UID 10001, und der Container konnte den Eigentümer nicht ändern (z. B. NFS-Freigabe, rootless Docker). Lösung:
sudo chown -R 10001:10001 /opt/memos/memos-dataoderMEMOS_UID/MEMOS_GIDpassend setzen, danachdocker compose restart memos. - „bind: address already in use“ auf Port 5230: Ein anderer Dienst belegt diesen Port. Lösung: Host-Port ändern, z. B.
"127.0.0.1:5231:5230", und den Reverse Proxy anpassen. - Share-Links und Webhooks zeigen falsche URLs:
MEMOS_INSTANCE_URLfehlt oder ist falsch. HTTPS-URL in der.enveintragen,docker compose up -d --force-recreate. - Instanz ungewollt öffentlich lesbar: Auf einer neuen Instanz mit gesetzter
MEMOS_INSTANCE_URLstartet Memos 0.31 im öffentlichen Zugriffsmodus. Lösung: in den Instanz-Einstellungen auf privat umstellen. - Daten nach Neustart weg: Das Volume-Mapping fehlt in der
compose.yaml. Ohne./memos-data:/var/opt/memosschreibt Memos in den ephemeralen Container-Layer. Volume-Zeile ergänzen,docker compose up -d. - MySQL-Verbindungsfehler beim Start: Bei
MEMOS_DRIVER=mysqlmussMEMOS_DSNexakt im Formatuser:pass@tcp(host:3306)/dbnamegesetzt sein. Ein fehlendestcp()führt zu „invalid DSN“. Zusätzlichdepends_onmitcondition: service_healthyergänzen. - Browser zeigt veraltetes Layout nach Update: Harter Reload mit Strg+Umschalt+R (Windows/Linux) oder Cmd+Umschalt+R (macOS) erzwingt das Nachladen aller Assets.
Häufige Fragen
Brauche ich eine externe Datenbank?
Nein. SQLite genügt für eine Instanz mit Einzelpersonen und kleinen Teams. MySQL 8+ oder PostgreSQL sind sinnvoll, wenn Sie einen bestehenden Datenbankserver nutzen möchten.
Welches Image-Tag soll ich verwenden?
Einen festen Versions-Tag wie 0.31.0, siehe Schritt 3. canary und latest nur auf Testsystemen.
Wie sichere ich meine Memos-Daten?
Bei gestopptem Container als Archiv von ./memos-data, siehe Schritt 6. Im laufenden Betrieb kopiert cp die SQLite-Datei nicht konsistent. Für automatisierte, versionierte Sicherungen siehe Restic Backup auf Linux automatisieren.
Wie aktiviere ich HTTPS?
Über einen Reverse Proxy, der an 127.0.0.1:5230 weiterleitet, und MEMOS_INSTANCE_URL mit der HTTPS-Adresse, siehe Schritt 5.
Läuft Memos auf einem Raspberry Pi?
Ja. Das Image gibt es für linux/arm64 und linux/arm/v7; dieselbe compose.yaml funktioniert ohne Änderungen.
Wie aktualisiere ich Memos auf eine neue Version?
Sichern, Tag in der compose.yaml anpassen, docker compose pull und docker compose up -d, siehe Schritt 6. Ein Downgrade nach der Migration ist nicht möglich.
Kann ich Memos mit MySQL oder PostgreSQL betreiben?
Ja. Setzen Sie MEMOS_DRIVER=mysql oder postgres und MEMOS_DSN, für MySQL im Format user:pass@tcp(host:3306)/dbname, für PostgreSQL postgres://user:pass@host:5432/memos?sslmode=disable. Das Datenvolume bleibt für Anhänge nötig.
Fazit
Memos ist mit einem Container und SQLite schnell eingerichtet und einfach zu betreiben. Für den Produktivbetrieb entscheidend sind ein fester Image-Tag, Zugriff nur über HTTPS, ein bewusst gewählter Zugriffsmodus mit gesperrter Registrierung und konsistente Sicherungen vor jedem Update. Für umfangreiche Dokumentation eignet sich ein Wiki wie BookStack oder AFFiNE besser.
Weiterführende Anleitungen und Quellen
- Docker und Docker Compose auf Linux installieren – die Self-Hosting-Grundlage
- Traefik als Docker-Reverse-Proxy mit automatischem HTTPS
- Restic Backup auf Linux und Windows einrichten und automatisieren
- Docker Compose absichern: Secrets, Healthchecks, Non-Root und Read-Only
- AFFiNE mit Docker installieren: All-in-One-Notion-Alternative
Offizielle Quellen: Memos Docker-Dokumentation (usememos.com) · Memos GitHub Repository · neosmemo/memos auf Docker Hub


