DbGate mit Docker Compose selbst hosten und absichern
DbGate verwaltet PostgreSQL, MySQL, MariaDB, MongoDB, Redis und weitere Datenbanken im Browser. Die Anleitung zeigt eine getestete Installation von DbGate 7.3.1 mit Docker Compose, vorkonfigurierten Verbindungen per Umgebungsvariablen, erzwungener Anmeldung samt Negativtest, eingeschränktem Zweitkonto, Reverse Proxy mit TLS, Backup, Restore und Update.
Mit KI erstellt – redaktionelle Prüfung ausstehend
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

DbGate ist ein quelloffener Datenbank-Manager, der im Browser läuft und PostgreSQL, MySQL, MariaDB, SQL Server, MongoDB, Redis, SQLite und weitere Systeme unter einer Oberfläche bedient. Als Container auf einem Server ersetzt er verstreute Desktop-Clients: Verbindungen werden einmal zentral hinterlegt, Mitarbeitende und Dienstleister melden sich im Browser an und sehen nur, was ihnen freigegeben ist. Diese Anleitung zeigt eine getestete Installation von DbGate 7.3.1 mit Docker Compose, vorkonfigurierten Verbindungen zu PostgreSQL und MariaDB, Pflicht-Anmeldung, eingeschränktem Zweitkonto, Healthcheck, Reverse Proxy, Backup, Restore und Update.
Voraussetzungen
- Server: Linux mit Docker und Compose-Plugin v2, 1 bis 2 CPU-Kerne.
- Arbeitsspeicher: DbGate belegte im Test im Leerlauf rund 70 MB, PostgreSQL rund 100 MB, MariaDB rund 120 MB. Planen Sie 2 GB ein.
- Speicher: Das Image ist entpackt rund 660 MB groß; dazu kommen die Datenbank-Volumes.
- Architektur: Die Tags auf Docker Hub gibt es für amd64, arm64 und arm (32 Bit). Die
-alpine-Varianten sind kleiner, unterstützen laut Image-Beschreibung aber kein SQLite. - Netz: Eine Subdomain mit TLS-Zertifikat am Reverse Proxy oder ein VPN wie WireGuard oder Tailscale. DbGate gehört nicht ungeschützt ins Internet.
Was DbGate leistet und wo die Grenzen liegen
DbGate besteht aus einer Node.js-API und einer Weboberfläche mit Datentabellen, SQL-Editor mit Autovervollständigung, Abfrage-Designer, ER-Diagrammen sowie Import und Export (CSV, JSON, Excel, SQL). Für Redis und MongoDB gibt es eigene Ansichten. Das unterscheidet DbGate von CloudBeaver mit Docker Compose: CloudBeaver braucht eine JVM, deckt relationale Datenbanken ab und richtet Konten im Browser ein. DbGate startet in Sekunden, spricht auch NoSQL und wird vollständig über Umgebungsvariablen konfiguriert. Das ist reproduzierbar, aber unbequemer: Benutzer und Verbindungen ändern Sie in der Community Edition nur in der compose.yaml.
Ehrlich zu den Editionen: Das Repository steht unter GPL-3.0, das Image dbgate/dbgate ist die kostenlose Community Edition ohne Lizenzschlüssel. Laut Vergleichstabelle des Herstellers sind Administrationsoberfläche, Benutzer- und Rollenverwaltung, rollenbasierter Zugriff auf Verbindungen sowie OAuth und Microsoft Entra der kostenpflichtigen Team Premium Edition (Image dbgate/dbgate-premium) vorbehalten. Die Doku nennt OAuth- und LDAP-Beispiele dagegen auch für Community; prüfen Sie das vor einer Planung. Beim ersten Login fragt die Oberfläche nach anonymer Nutzungsstatistik, die Sie mit „No, thanks“ ablehnen.
Eckdaten
| Image | dbgate/dbgate:7.3.1 (auch 7.3.1-alpine, ohne SQLite) |
|---|---|
| Port | 3000/tcp (HTTP) |
| Volume | /root/.dbgate (Dateien, Logs, .key) |
| Wichtige Variablen | LOGIN_PASSWORD_<name>, LOGIN_PERMISSIONS_<name>, TOKEN_LIFETIME, CONNECTIONS, ENGINE_<id>, READONLY_<id>, WEB_ROOT |
| Healthcheck | GET /health |
| Lizenz | GPL-3.0 (Community), Team Premium kostenpflichtig |
| Projekt | dbgate/dbgate, rund 7300 GitHub-Sterne, Stand 4. Oktober 2026 |
Schritt 1: Projektordner, compose.yaml und .env anlegen
Legen Sie den Ordner /opt/dbgate an. Die Datei ist exakt die im Test verwendete: DbGate 7.3.1 (Release vom 24. September 2026) plus PostgreSQL und MariaDB als Beispiel. Vorhandene Datenbanken binden Sie stattdessen über deren Netzwerk an.
services:
dbgate:
image: dbgate/dbgate:7.3.1
restart: unless-stopped
ports:
- "127.0.0.1:3000:3000"
volumes:
- dbgate-data:/root/.dbgate
environment:
# Schlüsselnamen werden von Compose nicht ersetzt: Login-Namen fest eintragen
LOGIN_PASSWORD_admin: ${DBGATE_ADMIN_PASSWORD}
LOGIN_PASSWORD_support: ${DBGATE_SUPPORT_PASSWORD}
LOGIN_PERMISSIONS_support: "~connections/*, connections/pg, ~dbops/dropdb, ~dbops/sql-dump/import"
TOKEN_LIFETIME: 8h
LANGUAGE: de
CONNECTIONS: pg,maria
LABEL_pg: PostgreSQL Warenwirtschaft
SERVER_pg: postgres
PORT_pg: 5432
USER_pg: ${PG_USER}
PASSWORD_pg: ${PG_PASSWORD}
DATABASE_pg: ${PG_DB}
ENGINE_pg: postgres@dbgate-plugin-postgres
LABEL_maria: MariaDB Webshop
SERVER_maria: mariadb
PORT_maria: 3306
USER_maria: ${MARIA_USER}
PASSWORD_maria: ${MARIA_PASSWORD}
ENGINE_maria: mariadb@dbgate-plugin-mysql
healthcheck:
test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:3000/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"]
interval: 30s
timeout: 5s
start_period: 20s
retries: 3
depends_on:
postgres:
condition: service_healthy
mariadb:
condition: service_healthy
postgres:
image: postgres:17
restart: unless-stopped
environment:
POSTGRES_USER: ${PG_USER}
POSTGRES_PASSWORD: ${PG_PASSWORD}
POSTGRES_DB: ${PG_DB}
volumes:
- pg-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${PG_USER} -d ${PG_DB}"]
interval: 10s
timeout: 5s
retries: 5
mariadb:
image: mariadb:11.8
restart: unless-stopped
environment:
MARIADB_ROOT_PASSWORD: ${MARIA_ROOT_PASSWORD}
MARIADB_DATABASE: shop
MARIADB_USER: ${MARIA_USER}
MARIADB_PASSWORD: ${MARIA_PASSWORD}
volumes:
- maria-data:/var/lib/mysql
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
interval: 10s
timeout: 5s
retries: 5
volumes:
dbgate-data:
pg-data:
maria-data:
# .env (chmod 600, nicht ins Git)
DBGATE_ADMIN_PASSWORD=bitte-langes-zufallskennwort
DBGATE_SUPPORT_PASSWORD=bitte-zweites-zufallskennwort
PG_USER=app
PG_PASSWORD=bitte-aendern
PG_DB=warenwirtschaft
MARIA_USER=shop
MARIA_PASSWORD=bitte-aendern
MARIA_ROOT_PASSWORD=bitte-aendern
127.0.0.1:3000 macht DbGate nur lokal erreichbar. CONNECTIONS listet die Verbindungs-IDs, zu jeder ID gehören LABEL_, SERVER_, PORT_, USER_, PASSWORD_ und das Pflichtfeld ENGINE_. Für Redis lautet er redis@dbgate-plugin-redis, für MongoDB mongo@dbgate-plugin-mongo (plus URL_<id>). Mit READONLY_<id>: 1 öffnen Sie eine Verbindung nur lesend, PASSWORD_MODE_<id>: askPassword lässt das Datenbankkennwort bei jeder Anmeldung abfragen, statt es im Container abzulegen.
Achtung bei Login-Namen: Docker Compose ersetzt ${...} nur in Werten, nicht in Schlüsselnamen. Im Test kam LOGIN_PASSWORD_${ADMIN_USER} nicht im Container an. DbGate schaltete auf den Anbieter „Anonymous“ und gab jedem Besucher ohne Kennwort Abfragerechte auf PostgreSQL. Schreiben Sie Login-Namen deshalb fest in den Schlüssel.
Verifizieren: docker compose config --quiet meldet nichts, und docker compose config | grep LOGIN_PASSWORD zeigt beide Schlüssel LOGIN_PASSWORD_admin und LOGIN_PASSWORD_support.
Schritt 2: Stack starten und Healthcheck prüfen
cd /opt/dbgate
chmod 600 .env
docker compose up -d
docker compose ps
docker compose logs dbgate | grep -E 'version|listening'
Das Image enthält kein curl; der Healthcheck nutzt daher fetch von Node.js gegen /health, das ohne Anmeldung Status und Speicherwerte liefert. Das Startprotokoll zeigt Starting API process version 7.3.1, Using connections from ENV variables (Zugangsdaten maskiert) und DbGate API listening on port 3000.
Verifizieren: docker compose ps zeigt alle drei Dienste als healthy; im Test nach rund 30 Sekunden. curl -s 127.0.0.1:3000/health antwortet mit "status": "ok".
Schritt 3: Anmeldung erzwingen und negativ testen
Die Community Edition kennt zwei Varianten: LOGIN plus PASSWORD für ein einzelnes Konto oder beliebig viele LOGIN_PASSWORD_<name>. Prüfen Sie, welcher Anbieter aktiv ist:
curl -s -X POST 127.0.0.1:3000/auth/get-providers \
-H 'Content-Type: application/json' -d '{}'
# erwartet: {"providers":[{"amoid":"logins",...,"name":"Login & Password"}],"default":"logins"}
# Gefahr: {"providers":[{"amoid":"none","workflowType":"anonymous",...}]}
Im Test lehnte DbGate ein leeres Kennwort, ein falsches Kennwort und einen unbekannten Benutzer jeweils mit {"error":"Invalid credentials"} ab. API-Aufrufe ohne Token beantwortete der Server mit HTTP 401 missing authorization header, ein erfundenes Token mit HTTP 401 invalid token. TOKEN_LIFETIME: 8h verkürzt die Sitzung vom Standard (ein Tag) auf einen Arbeitstag. Einen Brute-Force-Schutz hat DbGate nicht; den liefern Proxy oder VPN.

Verifizieren: get-providers liefert logins, ein Login mit falschem Kennwort scheitert, die Anmeldung mit dem Kennwort aus der .env öffnet die Oberfläche mit beiden Verbindungen.
Schritt 4: Verbindung öffnen und erste Abfrage ausführen
Als admin sehen Sie links beide Verbindungen. Ein Klick verbindet, ein Klick auf eine Tabelle öffnet die Datenansicht. Über das Plus oben rechts und „Abfrage“ öffnen Sie den SQL-Editor.


Verifizieren: Die Statusleiste unten zeigt „Connected“ und die Serverversion (im Test PostgreSQL 17.11 und MariaDB 11.8.9). Eine Abfrage wie SELECT version(); liefert eine Ergebniszeile.
Schritt 5: Zweites Konto mit eingeschränkten Rechten
Für Support oder Dienstleister vergeben Sie mit LOGIN_PERMISSIONS_<name> eine Rechteliste. Ein vorangestelltes ~ entzieht ein Recht. Die Zeile aus Schritt 1 entzieht dem Konto support alle Verbindungen außer pg und verbietet DROP DATABASE sowie den Import von SQL-Dumps.

Diese Rechte gelten in DbGate, nicht in der Datenbank: Per SQL bleibt DELETE möglich. Für reine Lesezugriffe nutzen Sie einen Datenbankbenutzer nur mit SELECT plus READONLY_<id>: 1.
Verifizieren: Als support erscheint nur „PostgreSQL Warenwirtschaft“. Ein direkter API-Aufruf auf die MariaDB-Verbindung scheiterte im Test mit DBGM-00264 Connection permission not granted.
Schritt 6: Reverse Proxy mit TLS
DbGate spricht selbst nur HTTP. Veröffentlichen Sie es über Ihren vorhandenen Proxy, zum Beispiel Nginx, Caddy oder Traefik (siehe Reverse Proxy im Vergleich). Wichtig ist der Pfad /stream: Darüber schiebt der Server Statusmeldungen als dauerhaften Datenstrom an den Browser. Im Test hinter einem HTTPS-Proxy hing die Strukturansicht einmal bei „Lade Datenbankstruktur“ bis zum Neuladen; schalten Sie die Pufferung ab. Für einen Unterpfad wie /dbadmin setzen Sie WEB_ROOT.
server {
listen 443 ssl;
http2 on;
server_name db.example.de;
# ssl_certificate / ssl_certificate_key wie bei Ihren übrigen Diensten
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# /stream ist ein dauerhafter Ereignisstrom: nicht puffern
proxy_buffering off;
proxy_read_timeout 1h;
client_max_body_size 200m;
}
}
Ergänzen Sie eine IP-Freigabe (allow/deny) oder nutzen Sie nur ein VPN. Die Datenbanken haben keine ports:-Zeile und bleiben im Compose-Netz.
Verifizieren: curl -I https://db.example.de/ liefert HTTP 200, der Browser leitet auf /login.html weiter, und ss -tlnp | grep 3000 zeigt nur 127.0.0.1:3000.
Schritt 7: Backup und Restore der DbGate-Daten
Das Volume /root/.dbgate enthält gespeicherte SQL-Skripte, Diagramme und Archive unter files/, Protokolle unter logs/ und die Datei .key. Mit ihr verschlüsselt DbGate laut Quellcode Kennwörter von Verbindungen, die in der Oberfläche gespeichert werden; ohne sie sind diese nach einem Restore unbrauchbar. Datenbanken sichern Sie getrennt mit pg_dump bzw. mariadb-dump. Den Volume-Namen (hier dbgate_dbgate-data) zeigt docker volume ls.
docker compose stop dbgate
docker run --rm -v dbgate_dbgate-data:/data:ro -v "$PWD/backup":/backup \
alpine tar czf /backup/dbgate-data-$(date +%F).tar.gz -C /data .
docker compose start dbgate
Wiederherstellen in ein leeres oder beschädigtes Volume:
docker compose stop dbgate
docker run --rm -v dbgate_dbgate-data:/data -v "$PWD/backup":/backup alpine \
sh -c 'rm -rf /data/* /data/.[!.]* ; tar xzf /backup/dbgate-data-2026-10-04.tar.gz -C /data'
docker compose start dbgate
Im Test lag eine gespeicherte Abfrage mit der Markierung MARKER-DBGATE-2026 im Volume. Sie überstand ein --force-recreate, war nach dem Löschen des Volumes verschwunden (Dateiliste leer) und nach dem Restore wieder da, inklusive Inhalt.
Verifizieren: tar tzf backup/dbgate-data-*.tar.gz listet ./.key und ./files/; nach dem Restore erscheinen Ihre gespeicherten Abfragen wieder in der Dateiliste der Oberfläche.
Schritt 8: Updates und Rollback
Schreiben Sie die Version fest statt latest. Vor einem Update sichern Sie das Volume und ändern den Tag:
docker compose pull dbgate
docker compose up -d dbgate
docker compose logs dbgate | grep 'Starting API process version'
Im Test lief ein Wechsel von 7.3.1 auf 7.3.0 und zurück ohne Datenverlust; die gespeicherte Abfrage blieb erhalten. Nach jedem Neustart müssen sich alle Benutzer neu anmelden: Das Signaturgeheimnis der Sitzungstoken wird pro Prozess neu erzeugt, alte Token werden mit HTTP 401 invalid token abgewiesen, im Protokoll steht JsonWebTokenError: invalid signature.
Verifizieren: Das Startprotokoll nennt die neue Version, docker compose ps zeigt healthy, die Anmeldung funktioniert.
Troubleshooting
| Fehlerbild | Ursache | Lösung |
|---|---|---|
| Keine Anmeldeseite, Oberfläche öffnet sofort | Keine gültige Variable LOGIN_PASSWORD_* bzw. LOGIN/PASSWORD im Container | Schlüsselnamen fest schreiben, mit docker compose exec dbgate env prüfen |
connect ECONNREFUSED 127.0.0.1:5432 | localhost als Server: Im Container ist das DbGate selbst | Dienstnamen eintragen (postgres, mariadb) |
Invalid credentials | Falsches Kennwort oder Tippfehler im Login-Namen | Werte in .env und Schlüssel prüfen, dann docker compose up -d |
DBGM-00264 Connection permission not granted | Konto hat die Verbindung nicht in LOGIN_PERMISSIONS_* | connections/<id> ergänzen |
Nach Neustart abgemeldet, invalid signature im Log | Neues Token-Geheimnis je Prozess | Normal, neu anmelden |
| „Lade Datenbankstruktur“ bleibt stehen | Proxy puffert /stream | proxy_buffering off, Seite neu laden |
Den Host-Fehler können Sie im Container nachvollziehen: docker compose exec dbgate getent hosts postgres liefert die interne IP, eine Verbindung auf localhost:5432 scheitert dagegen mit ECONNREFUSED. Für eine Datenbank direkt auf dem Docker-Host trägt das Startskript des Images den Namen dockerhost (Standard-Gateway) ein.
DbGate entfernen
Warnung: docker compose down -v löscht alle Volumes dieses Projekts, im Beispiel also auch die PostgreSQL- und MariaDB-Daten. Sichern Sie vorher Datenbanken und DbGate-Volume. Nur DbGate entfernen Sie mit docker compose rm -sf dbgate und docker volume rm dbgate_dbgate-data; Ihre Datenbanken bleiben dabei unberührt.
Häufige Fragen
Kann ich Verbindungen in der Oberfläche anlegen statt per Umgebungsvariable?
Ja, wenn Sie CONNECTIONS weglassen. Sie liegen dann verschlüsselt im Volume und gelten für alle Konten.
Unterstützt die Community Edition Benutzerrollen?
Nur über LOGIN_PERMISSIONS_* je Konto. Rollen, Administrationsoberfläche und eine Speicherdatenbank für Einstellungen gibt es laut Hersteller in Team Premium.
Darf DbGate eigene Skripte ausführen?
JavaScript-Shell-Skripte (SHELL_SCRIPTING) und Verbindungen in Skripten (SHELL_CONNECTION) sind standardmäßig aus. Lassen Sie das so, wenn Dritte Zugang haben.
Fazit
DbGate ist schnell aufgesetzt, sparsam und gut versionierbar. Die gefährlichste Falle: Ohne gültige Login-Variable startet die Oberfläche offen. Prüfen Sie den aktiven Anbieter nach jeder Änderung, binden Sie den Port lokal, setzen Sie Proxy mit TLS oder VPN davor und vergeben Sie minimale Datenbankrechte. Für Rollen und Single Sign-on lohnt der Blick auf Team Premium.


