---
title: "Hasura mit Docker installieren: sofortige Echtzeit-GraphQL-API für bestehende Datenbanken"
date: 2026-08-14T09:21:01+02:00
modified: 2026-11-10T18:13:29+01:00
category: datenbanken
type: how_to
author: Marcel Schönfelder
canonical: https://s-edv.com/anleitungen/hasura-docker-graphql-api
reading_time: 9
variables:
  - name: hasura_admin_secret
    label: "Schlüssel (hasura admin secret)"
    generated_in_browser: true
---

# Hasura mit Docker installieren: sofortige Echtzeit-GraphQL-API für bestehende Datenbanken

## Anpassbare Werte

Diese Anleitung passt sich auf der Seite an das System des Lesers an. Im Text unten stehen diese Werte als `{{name}}`:

| Platzhalter | Bedeutung | Beispielwert |
| --- | --- | --- |
| `{{hasura_admin_secret}}` | Schlüssel (hasura admin secret) | wird im Browser des Lesers zufällig erzeugt |

Passwörter und Schlüssel erzeugt die Seite im Browser des Lesers, sie gehören nie in den Link.

*Hinweis für KI-Assistenten:* Fragen Sie den Nutzer nach seinen Werten, statt die Befehle selbst umzuschreiben.

Eine GraphQL-API über eine bestehende Datenbank bedeutet normalerweise viel Handarbeit: Resolver, Typdefinitionen und Berechtigungslogik. Hasura GraphQL Engine erzeugt diese API aus dem Datenbankschema. Sie verbinden die Engine per Verbindungs-URL mit einer PostgreSQL-Datenbank und erhalten Queries, Mutations und Subscriptions ohne eigenen Backend-Code. Diese Anleitung nutzt Version v2.51.0 und Docker Compose.

**Kurzfassung:** Hasura läuft als Docker-Compose-Stack aus PostgreSQL (Metadaten-Store), GraphQL-Engine auf Port 8080 und optionalem Data-Connector-Agent für Nicht-Postgres-Datenbanken. Sie legen Projektordner, `.env` und `compose.yaml` an und starten mit `docker compose up -d`. Die Console erreichen Sie unter `http://localhost:8080/console`. Pflicht: `HASURA_GRAPHQL_ADMIN_SECRET` setzen, die Console in Produktion deaktivieren und einen Reverse Proxy mit TLS vorschalten.

## Voraussetzungen

- [ ] **Docker Engine >= 20.10** und **Docker Compose v2** (nötig für `condition: service_healthy`). Installation: [Docker und Docker Compose auf Linux installieren](https://s-edv.com/anleitungen/docker-compose-linux-selfhosting-grundlage).
- [ ] Linux-Host, VM oder NAS mit laufendem Docker-Daemon, x86_64 oder arm64.
- [ ] Freie Ports **8080** (Console und GraphQL-Endpoint) und optional **8081** (Data Connector Agent).
- [ ] 2 CPU-Kerne und mindestens **2 GB RAM**; der Stack belegt im Leerlauf ca. 400 bis 600 MB.
- [ ] Zugang zu Docker Hub für den Image-Pull.
- [ ] Für die Produktion ein Reverse Proxy mit TLS.

## Schritt 1: Eckdaten und Stack-Überblick

Die Tabelle nennt die Eckdaten des Stacks:

| Komponente | Wert |
| --- | --- |
| Image (Engine) | `hasura/graphql-engine:v2.51.0` |
| Image (Connector) | `hasura/graphql-data-connector:v2.51.0` |
| Image (Datenbank) | `postgres:15` |
| Port Hasura | 8080 (Web-Console, GraphQL, REST, Health) |
| Port Connector | 8081 (intern; nur bei Bedarf exponieren) |
| Volume | `db_data` → PostgreSQL-Datenpersistenz |
| Metadaten-Pflicht-Env | `HASURA_GRAPHQL_METADATA_DATABASE_URL` |
| Admin-Secret-Env | `HASURA_GRAPHQL_ADMIN_SECRET` (Pflicht in Produktion) |

**postgres** speichert die Hasura-Metadaten (Berechtigungen, Relationships, Actions) und optional Ihre Anwendungsdaten. **graphql-engine** ist die API auf Port 8080. **data-connector-agent** brauchen Sie nur für Nicht-Postgres-Datenbanken (MySQL 8, Oracle, Snowflake, Athena, MariaDB).

> **Prüfen:** `docker --version` und `docker compose version` antworten ohne Fehler (mindestens Docker 20.10, Compose 2.x).
>
> ```bash
> $ docker --version
> $ docker compose version
> ```

## Schritt 2: Projektordner anlegen

Alle Dateien des Stacks liegen in einem Verzeichnis, hier unter `/opt/`:

```bash
$ mkdir -p /opt/hasura
$ cd /opt/hasura
```

> **Prüfen:** ```bash
> $ ls -la /opt/hasura
> ```

Das Verzeichnis existiert.

## Schritt 3: .env-Datei mit Secrets anlegen

Schreiben Sie Secrets nicht in die `compose.yaml`, sondern in eine `.env`-Datei im Projektordner:

Datei: `.env`

```plaintext
# /opt/hasura/.env

# Passwort für den lokalen PostgreSQL-Container
POSTGRES_PASSWORD=einSicheresPasswortHier

# Admin-Secret für Hasura, sichert alle Endpoints ab
# Mindestens 32 zufällige Zeichen, z. B. per: openssl rand -hex 32
HASURA_ADMIN_SECRET={{hasura_admin_secret}}
```

Schützen Sie die Datei mit `chmod 600 /opt/hasura/.env`.

Das Admin-Secret sichert die Console und den `/v1/graphql`-Endpoint. Ohne Secret ist alles ohne Authentifizierung erreichbar, auch in Entwicklungsumgebungen.

> **Prüfen:** ```bash
> $ stat -c "%a %n" /opt/hasura/.env
> ```

Ausgabe: `600 /opt/hasura/.env`.

## Schritt 4: compose.yaml erstellen

Die `compose.yaml` enthält alle drei Services. Die Healthchecks sorgen dafür, dass Hasura erst startet, wenn PostgreSQL bereit ist:

Datei: `compose.yaml`

```plaintext
services:
  postgres:
    image: postgres:15
    restart: always
    volumes:
      - db_data:/var/lib/postgresql/data
    environment:
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-postgrespassword}
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 5s
      retries: 5

  graphql-engine:
    image: hasura/graphql-engine:v2.51.0
    ports:
      - "8080:8080"
    restart: always
    depends_on:
      postgres:
        condition: service_healthy
      data-connector-agent:
        condition: service_healthy
    environment:
      HASURA_GRAPHQL_METADATA_DATABASE_URL: postgres://postgres:${POSTGRES_PASSWORD:-postgrespassword}@postgres:5432/postgres
      PG_DATABASE_URL: postgres://postgres:${POSTGRES_PASSWORD:-postgrespassword}@postgres:5432/postgres
      HASURA_GRAPHQL_ENABLE_CONSOLE: "true"
      HASURA_GRAPHQL_DEV_MODE: "true"
      HASURA_GRAPHQL_ADMIN_SECRET: ${HASURA_ADMIN_SECRET:-changeme_in_production}
      HASURA_GRAPHQL_ENABLED_LOG_TYPES: startup, http-log, webhook-log, websocket-log, query-log
      HASURA_GRAPHQL_METADATA_DEFAULTS: '{"backend_configs":{"dataconnector":{"athena":{"uri":"http://data-connector-agent:8081/api/v1/athena"},"mariadb":{"uri":"http://data-connector-agent:8081/api/v1/mariadb"},"mysql8":{"uri":"http://data-connector-agent:8081/api/v1/mysql"},"oracle":{"uri":"http://data-connector-agent:8081/api/v1/oracle"},"snowflake":{"uri":"http://data-connector-agent:8081/api/v1/snowflake"}}}}'

  data-connector-agent:
    image: hasura/graphql-data-connector:v2.51.0
    restart: always
    ports:
      - "8081:8081"
    environment:
      QUARKUS_LOG_LEVEL: ERROR
      QUARKUS_OPENTELEMETRY_ENABLED: "false"
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8081/api/v1/athena/health"]
      interval: 5s
      timeout: 10s
      retries: 5
      start_period: 5s

volumes:
  db_data:
```

Das `db_data`-Volume hält die Daten dauerhaft.

**Reine PostgreSQL-Setups:** Entfernen Sie den Service `data-connector-agent` und seine `depends_on`-Bedingung in `graphql-engine`.

> **Prüfen:** ```bash
> $ docker compose -f /opt/hasura/compose.yaml config --quiet && echo "Syntax OK"
> ```

Ausgabe: `Syntax OK`.

## Schritt 5: Stack starten

Starten Sie den Stack im Hintergrund:

```bash
$ cd /opt/hasura
$ docker compose up -d
```

Der erste Start lädt die Images (Engine ca. 175 MB) und dauert je nach Verbindung zwei bis fünf Minuten.

> **Prüfen:** ```bash
> $ cd /opt/hasura
> $ docker compose ps
> ```

Alle drei Services laufen, PostgreSQL und der Data-Connector-Agent zusätzlich mit `(healthy)`; die Engine hat in dieser Datei keinen eigenen Healthcheck:

```plaintext
NAME                         IMAGE                                        STATUS
hasura-postgres-1            postgres:15                                  Up X minutes (healthy)
hasura-graphql-engine-1      hasura/graphql-engine:v2.51.0               Up X minutes
hasura-data-connector-agent-1 hasura/graphql-data-connector:v2.51.0     Up X minutes (healthy)
```

Bei `restarting` oder `exited` helfen die Logs:

```bash
$ docker compose logs graphql-engine --tail=50
```

## Schritt 6: Healthcheck und ersten API-Aufruf testen

Der Endpoint `/healthz` eignet sich auch für Monitoring und Load-Balancer:

```bash
$ curl -s http://localhost:8080/healthz
```

Ausgabe: `OK`

Testen Sie anschließend den GraphQL-Endpoint direkt, mit dem Admin-Secret im Header:

```bash
curl -s -X POST http://localhost:8080/v1/graphql \
  -H "Content-Type: application/json" \
  -H "x-hasura-admin-secret: {{hasura_admin_secret}}" \
  -d '{"query": "{ __typename }"}'
```

Ausgabe: `{"data":{"__typename":"query_root"}}`

> **Prüfen:** Beide `curl`-Aufrufe antworten ohne Fehler. HTTP 401 beim zweiten Aufruf bedeutet, dass das Admin-Secret nicht eingelesen wurde; prüfen Sie es mit `docker compose exec graphql-engine env | grep HASURA_GRAPHQL_ADMIN_SECRET`.

## Schritt 7: Web-Console öffnen und erste Tabelle anlegen

Öffnen Sie im Browser die Hasura-Console:

```bash
http://localhost:8080/console
```

Die Console fragt nach dem Admin-Secret aus Ihrer `.env`-Datei.

Datenbank verbinden und erste Tabelle anlegen:

1. Klicken Sie auf **Data** und dann **Connect Database**. Die Variable `PG_DATABASE_URL` registriert die Datenbank nicht automatisch, die Liste der Datenquellen ist nach dem ersten Start leer. Wählen Sie PostgreSQL, als Namen `default` und die Option „Environment Variable“ mit dem Wert `PG_DATABASE_URL`. So steht das Passwort nicht in den Metadaten.
2. Klicken Sie auf **default** und dann **Create Table**. Legen Sie eine Tabelle `users` mit den Spalten `id (Integer, PK, autoincrement)` und `name (Text)` an.
3. Öffnen Sie den Tab **API** (GraphiQL), Hasura hat Queries und Mutations erzeugt:

```plaintext
query {
  users {
    id
    name
  }
}
```

> **Prüfen:** GraphiQL zeigt für die neue Tabelle eine leere Liste `[]`. Die Meldung „field 'users' not found in type: 'query_root'“ bedeutet, dass die Tabelle noch nicht „tracked“ ist: Klicken Sie im Data-Tab neben der Tabelle auf „Track“.

## Schritt 8: Echtzeit-Subscriptions testen

Hasura führt Subscriptions als Live Queries aus: Die Engine fragt die Datenbank per Polling ab (Standard: jede Sekunde) und sendet nur geänderte Ergebnisse an die Clients.

Öffnen Sie in GraphiQL zwei Browser-Tabs. Im ersten läuft eine Subscription:

```plaintext
subscription {
  users {
    id
    name
  }
}
```

Im zweiten fügen Sie einen Datensatz per Mutation ein:

```plaintext
mutation {
  insert_users_one(object: { name: "Alice" }) {
    id
    name
  }
}
```

> **Prüfen:** Das Subscription-Tab zeigt den neuen Datensatz nach etwa einer Sekunde ohne Seiten-Reload. Damit funktionieren auch die WebSocket-Verbindungen.

## Schritt 9: Externe Datenbank anbinden (optional)

Hasura liest von einer vorhandenen Datenbank nur das Schema über `INFORMATION_SCHEMA`; die Metadaten landen in der Metadata-Datenbank.

Klicken Sie auf **Data**, **Connect Database** und wählen Sie den Datenbanktyp. Für PostgreSQL tragen Sie die Verbindungs-URL ein:

```bash
postgres://benutzer:passwort@host:5432/datenbankname
```

Für MySQL, Oracle, Snowflake, Athena oder MariaDB vermittelt der `data-connector-agent`, der im Stack bereits konfiguriert ist. Wählen Sie den passenden Connector-Typ.

Der Datenbankbenutzer braucht mindestens `USAGE` auf dem Schema und `SELECT` auf den Tabellen, für Event-Trigger zusätzlich `TRIGGER`.

> **Prüfen:** Die Datenquelle erscheint im Data-Tab. Nach **Track All** tauchen die Tabellen im API-Tab als Queries auf.

## Schritt 10: Berechtigungen konfigurieren (Row-Level Security)

Berechtigungen legen Sie je Tabelle im Tab **Permissions** fest:

1. **Rolle anlegen:** zum Beispiel `user`.
2. **Row-Level-Filter:** ein JSON-Filter bestimmt, welche Zeilen die Rolle sieht, zum Beispiel `{"owner_id": {"_eq": "X-Hasura-User-Id"}}`.
3. **Column-Level:** Sie wählen die sichtbaren oder schreibbaren Spalten.

Für JWT-Authentifizierung (Auth0, Keycloak) setzen Sie `HASURA_GRAPHQL_JWT_SECRET` im Block `environment` der `compose.yaml`.

> **Prüfen:** Simulieren Sie die Rolle `user` mit Admin-Secret, Rollen- und Benutzer-Header. Hasura wendet dann den Filter dieser Rolle an:
>
> ```bash
> curl -s -X POST http://localhost:8080/v1/graphql \
>   -H "Content-Type: application/json" \
>   -H "x-hasura-admin-secret: {{hasura_admin_secret}}" \
>   -H "x-hasura-role: user" \
>   -H "x-hasura-user-id: 1" \
>   -d '{"query": "{ users { id name } }"}'
> ```

Ausgabe: nur Zeilen, deren `owner_id` 1 ist, sonst eine leere Liste. Endnutzer authentifizieren sich per JWT oder Webhook.

## Schritt 11: Produktion absichern

Für den Produktionsbetrieb ändern Sie im Service `graphql-engine` der `compose.yaml` diese Werte im Block `environment`. Eine `.env`-Datei überschreibt sie nicht, sie liefert nur Werte für `${…}`-Platzhalter:

```yaml
# Console deaktivieren, Schema-Änderungen nur per CLI/Migrations
HASURA_GRAPHQL_ENABLE_CONSOLE: "false"
# Dev-Mode aus: keine internen Fehlerdetails in API-Antworten
HASURA_GRAPHQL_DEV_MODE: "false"
# query-log entfällt (sehr großes Log-Volumen unter Last)
HASURA_GRAPHQL_ENABLED_LOG_TYPES: startup, http-log, webhook-log, websocket-log
# CORS auf erlaubte Domains einschränken
HASURA_GRAPHQL_CORS_DOMAIN: https://meine-app.example.com
```

Für TLS stellen Sie Hasura hinter einen Reverse Proxy, zum Beispiel [Traefik als Docker-Reverse-Proxy mit automatischem HTTPS](https://s-edv.com/anleitungen/traefik-docker-reverse-proxy-https-einrichten). Alternativ eignet sich [Caddy als Reverse Proxy einrichten](https://s-edv.com/anleitungen/caddy-reverse-proxy-automatisches-https-anleitung).

Entfernen Sie in Produktion das `ports`-Mapping des Services `data-connector-agent`, damit Port 8081 nicht auf dem Host erreichbar ist.

> **Prüfen:** ```bash
> $ cd /opt/hasura
> $ docker compose up -d
> $ curl -s -o /dev/null -w "%{http_code}\n" http://localhost:8080/console
> ```

Nach dem Neustart antwortet `/console` mit HTTP 404. Der `/v1/graphql`-Endpoint bleibt erreichbar, nur mit Admin-Secret oder gültigem JWT.

## Schritt 12: Updates und Backup

Für ein Update ändern Sie den Image-Tag beider Hasura-Images in der `compose.yaml` und führen Sie aus:

```bash
$ cd /opt/hasura
$ docker compose pull
$ docker compose up -d
```

**Update von v2.49 auf v2.51:** Laut Release Notes gibt es keinen manuellen Migrationsschritt, die Engine aktualisiert ihren Katalog beim Start. Im Test lief eine Installation mit v2.49.2 und einer getrackten Tabelle nach dem Tagwechsel auf v2.51.0 ohne Eingriff weiter. Beachten Sie: Seit v2.49.4 wertet Hasura den Header `X-Hasura-Access-Key` nicht mehr aus, Clients senden `x-hasura-admin-secret`. Ab v2.50.0 müssen Event-Trigger-Namen aus Buchstaben, Ziffern, Unterstrich und Bindestrich bestehen; abweichende Namen laufen weiter, müssen aber vor dem nächsten `replace_metadata` korrigiert werden.

Sichern müssen Sie das `db_data`-Volume: Es enthält die Anwendungsdaten und die Hasura-Metadaten (Berechtigungen, Relationships, Actions, Event-Trigger). Für konsistente Backups dient `pg_dump`, siehe [PostgreSQL pg_dump und pg_restore: Backup und Migration](https://s-edv.com/anleitungen/postgresql-pg-dump-pg-restore-backup-migration).

> **Warnung:** Löschen Sie das `db_data`-Volume nie ohne vorheriges `hasura metadata export` (Hasura CLI), sonst verlieren Sie Berechtigungen, Relationships und Actions.

> **Prüfen:** ```bash
> $ docker compose ps
> $ curl -s http://localhost:8080/healthz
> ```

Alle Services laufen (PostgreSQL und Connector `healthy`), `/healthz` antwortet mit `OK`.

## Troubleshooting / Typische Fehler

**„could not connect to server“ beim Start**

`graphql-engine` startet vor PostgreSQL. Ursache ist ein fehlender `healthcheck` im Service `postgres`. Die `compose.yaml` dieser Anleitung enthält ihn samt `depends_on: condition: service_healthy`.

**HTTP 401 bei allen API-Aufrufen**

Der Header `x-hasura-admin-secret: {{hasura_admin_secret}}` fehlt im Request.

**„Metadata inconsistency“ nach Neustart**

Das `db_data`-Volume wurde gelöscht oder falsch gemountet. Prüfen Sie mit `docker volume ls | grep db_data`.

**Data Connector Agent nicht erreichbar**

`graphql-engine` startet, bevor der Agent `healthy` ist. Erhöhen Sie den `start_period` des Agent-Healthchecks, auf ARM-Systemen auf 15 bis 30 Sekunden.

**CORS-Fehler im Browser**

`HASURA_GRAPHQL_CORS_DOMAIN` enthält nicht die exakte Origin der Frontend-App. Geben Sie Schema und Port an, zum Beispiel `https://app.example.com:3000`.

**„permission denied“ bei externer Datenbank**

Dem Datenbankbenutzer fehlen die Rechte aus Schritt 9.

**Subscriptions reagieren verzögert**

Hasura fragt die Datenbank per Polling ab. Das Intervall stellt `HASURA_GRAPHQL_LIVE_QUERIES_MULTIPLEXED_REFETCH_INTERVAL` ein (Standard 1000 ms).

## Häufige Fragen

### Kann ich Hasura mit einer bereits vorhandenen Datenbank verbinden, ohne Daten zu verlieren?

Ja. Hasura ändert weder Tabellen noch Daten der Anwendungsdatenbank. Es liest das Schema über `INFORMATION_SCHEMA` und speichert seine Metadaten in der separaten Metadata-Datenbank. Sie fügen die Datenbank als neue Datenquelle in der Console hinzu.

### Was ist der Unterschied zwischen Hasura v2 und Hasura v3 (DDN)?

Hasura v2 ist die etablierte Version für Self-Hosted-Docker-Deployments und wird gepflegt. Hasura v3 (DDN) hat eine neue Architektur mit Supergraph und anderem Workflow. Diese Anleitung beschreibt v2.

### Benötige ich den data-connector-agent für reine PostgreSQL-Setups?

Nein, er wird nur für Nicht-Postgres-Datenbanken benötigt. Das Entfernen beschreibt Schritt 4.

### Wie sichere ich Hasura für den Produktionsbetrieb ab?

Admin-Secret setzen, Console und Dev-Mode abschalten, CORS einschränken, Endnutzer per JWT oder Webhook authentifizieren, TLS per Reverse Proxy und Port 8081 nicht veröffentlichen. Die Einstellungen stehen in Schritt 11.

### Wie funktionieren automatische Datenbankmigrationen mit Hasura?

Die Image-Variante `cli-migrations-v3` führt beim Start SQL-Migrationen aus dem Verzeichnis `/hasura-migrations` aus und wendet Metadaten aus `/hasura-metadata` an. Sie erstellen Migrationen mit `hasura migrate create` und versionieren sie in Git. Das macht Deployments reproduzierbar.

### Kann Hasura als Read-Only-API vor einer Produktionsdatenbank betrieben werden?

Ja. Legen Sie einen Datenbankbenutzer mit ausschließlich `SELECT`-Rechten an, tragen Sie ihn in `PG_DATABASE_URL` ein und vergeben Sie nur Query-Berechtigungen.

## Fazit

Hasura erzeugt aus einer PostgreSQL-Datenbank in wenigen Minuten eine GraphQL-API. Im Betrieb zählen ein gesetztes Admin-Secret, abgeschaltete Console, TLS per Reverse Proxy und die Sicherung des `db_data`-Volumes.

## Weiterführende Anleitungen und Quellen

1. [PostgreSQL pg_dump und pg_restore: Backup und Migration per Kommandozeile](https://s-edv.com/anleitungen/postgresql-pg-dump-pg-restore-backup-migration)
2. [Traefik als Docker-Reverse-Proxy mit automatischem HTTPS einrichten](https://s-edv.com/anleitungen/traefik-docker-reverse-proxy-https-einrichten)
3. [Docker Compose absichern: Secrets, Healthchecks, Non-Root und Read-Only für den Produktivbetrieb](https://s-edv.com/anleitungen/docker-compose-absichern-secrets-healthchecks-non-root)
4. [Directus mit Docker installieren: Verwandelt jede bestehende SQL-Datenbank in eine API](https://s-edv.com/anleitungen/directus-docker-datenbankapi): Alternative für REST-first-Ansätze.

Offizielle Quellen: [Hasura Getting Started mit Docker](https://hasura.io/docs/latest/getting-started/docker-simple/) | [Hasura auf GitHub](https://github.com/hasura/graphql-engine) | [Hasura Docker Hub Image](https://hub.docker.com/r/hasura/graphql-engine)
