{
  "schema_version": 1,
  "generated_at": "2026-10-23T00:00:00.000Z",
  "canonical_url": "https://openlogs.fyi/tools/nhost",
  "slug": "nhost",
  "name": "Nhost",
  "tagline": "Backend platform with Postgres, a GraphQL API, authentication and file storage.",
  "replaces": {
    "summary": "Paid backend-as-a-service services, such as Firebase.",
    "products": [
      {
        "slug": "firebase",
        "name": "Firebase",
        "vendor": "Google",
        "url": "https://openlogs.fyi/alternatives/firebase"
      }
    ]
  },
  "stack": [
    {
      "kind": "language",
      "name": "TypeScript"
    },
    {
      "kind": "database",
      "name": "PostgreSQL"
    }
  ],
  "deployment_methods": [
    "docker_compose"
  ],
  "requirements": {
    "min_ram_mb": null,
    "min_cpu_cores": null,
    "source_url": null
  },
  "license": {
    "spdx": "MIT",
    "class": "osi",
    "source_url": "https://github.com/nhost/nhost/blob/e5845916dcec56e1dc22b19be45ef01e954416e2/LICENSE"
  },
  "business_model": {
    "value": "unknown",
    "notes": "The README at the pinned commit does not state a business model, so it stays unknown.",
    "source_url": "https://github.com/nhost/nhost/blob/e5845916dcec56e1dc22b19be45ef01e954416e2/README.md",
    "checked_at": "2026-10-22"
  },
  "difficulty": {
    "value": "hard",
    "reason": "10 services (2 database or broker: minio, postgres), 10 volume mounts, 13 env values to set, host access: docker_socket. Heuristic score 10 of 11: hard. A source checkout is needed, so the rating is at least medium."
  },
  "repository": {
    "owner": "nhost",
    "name": "nhost",
    "url": "https://github.com/nhost/nhost"
  },
  "compose_check": {
    "status": "config_ok",
    "validated_at": "2026-10-22",
    "summary": "config_ok on 2026-10-22"
  },
  "recipe": {
    "version": 1,
    "source_path": "examples/docker-compose/docker-compose.yaml",
    "source_repo": "nhost/nhost",
    "source_commit_sha": "e5845916dcec56e1dc22b19be45ef01e954416e2",
    "source_kind": "app_repo",
    "source_url": "https://github.com/nhost/nhost/blob/e5845916dcec56e1dc22b19be45ef01e954416e2/examples/docker-compose/docker-compose.yaml"
  },
  "install_steps": [
    {
      "order": 1,
      "kind": "prerequisite",
      "language": "text",
      "label": "Docker with the Compose plugin and git",
      "body": "A host with Docker, the Docker Compose plugin and git. This entry needs a checkout of the project repository.",
      "source_url": "https://github.com/nhost/nhost/blob/e5845916dcec56e1dc22b19be45ef01e954416e2/examples/docker-compose/README.md",
      "validation_status": "unvalidated"
    },
    {
      "order": 2,
      "kind": "shell",
      "language": "bash",
      "label": "Get the source at commit e584591",
      "body": "git init nhost\ncd nhost\ngit remote add origin https://github.com/nhost/nhost.git\ngit sparse-checkout init --cone\ngit sparse-checkout set examples/docker-compose\ngit fetch --depth 1 --filter=blob:none origin e5845916dcec56e1dc22b19be45ef01e954416e2\ngit checkout FETCH_HEAD",
      "source_url": "https://github.com/nhost/nhost/blob/e5845916dcec56e1dc22b19be45ef01e954416e2/examples/docker-compose/README.md",
      "validation_status": "unvalidated"
    },
    {
      "order": 3,
      "kind": "shell",
      "language": "bash",
      "label": "Go to the folder the docs use",
      "body": "cd examples/docker-compose",
      "source_url": "https://github.com/nhost/nhost/blob/e5845916dcec56e1dc22b19be45ef01e954416e2/examples/docker-compose/README.md",
      "validation_status": "unvalidated"
    },
    {
      "order": 4,
      "kind": "compose",
      "language": "yaml",
      "label": "The compose file in the checkout: examples/docker-compose/docker-compose.yaml (shown here for reading)",
      "body": "services:\n    auth:\n        image: nhost/auth:0.40.2\n        depends_on:\n            graphql:\n                condition: service_healthy\n            postgres:\n                condition: service_healthy\n        environment:\n            AUTH_ACCESS_CONTROL_ALLOWED_EMAIL_DOMAINS: \"\"\n            AUTH_ACCESS_CONTROL_ALLOWED_EMAILS: \"\"\n            AUTH_ACCESS_CONTROL_ALLOWED_REDIRECT_URLS: \"\"\n            AUTH_ACCESS_CONTROL_BLOCKED_EMAIL_DOMAINS: \"\"\n            AUTH_ACCESS_CONTROL_BLOCKED_EMAILS: \"\"\n            AUTH_ACCESS_TOKEN_EXPIRES_IN: \"900\"\n            AUTH_ANONYMOUS_USERS_ENABLED: \"false\"\n            AUTH_API_PREFIX: /v1\n            AUTH_CLIENT_URL: http://localhost:3000\n            AUTH_CONCEAL_ERRORS: \"false\"\n            AUTH_DISABLE_NEW_USERS: \"false\"\n            AUTH_DISABLE_SIGNUP: \"false\"\n            AUTH_EMAIL_PASSWORDLESS_ENABLED: \"false\"\n            AUTH_EMAIL_SIGNIN_EMAIL_VERIFIED_REQUIRED: \"${AUTH_EMAIL_SIGNIN_EMAIL_VERIFIED_REQUIRED}\"\n            AUTH_GRAVATAR_DEFAULT: blank\n            AUTH_GRAVATAR_ENABLED: \"true\"\n            AUTH_GRAVATAR_RATING: g\n            AUTH_HOST: 0.0.0.0\n            AUTH_JWT_CUSTOM_CLAIMS: '{}'\n            AUTH_LOCALE_ALLOWED_LOCALES: en\n            AUTH_LOCALE_DEFAULT: en\n            AUTH_MFA_ENABLED: \"false\"\n            AUTH_MFA_TOTP_ISSUER: \"\"\n            AUTH_OTP_EMAIL_ENABLED: \"false\"\n            AUTH_PASSWORD_HIBP_ENABLED: \"false\"\n            AUTH_PASSWORD_MIN_LENGTH: \"9\"\n            AUTH_PORT: \"4000\"\n            AUTH_RATE_LIMIT_BRUTE_FORCE_BURST: \"10\"\n            AUTH_RATE_LIMIT_BRUTE_FORCE_INTERVAL: 5m\n            AUTH_RATE_LIMIT_EMAIL_BURST: \"10\"\n            AUTH_RATE_LIMIT_EMAIL_INTERVAL: 1h\n            AUTH_RATE_LIMIT_EMAIL_IS_GLOBAL: \"true\"\n            AUTH_RATE_LIMIT_ENABLE: \"true\"\n            AUTH_RATE_LIMIT_GLOBAL_BURST: \"100\"\n            AUTH_RATE_LIMIT_GLOBAL_INTERVAL: 1m\n            AUTH_RATE_LIMIT_SIGNUPS_BURST: \"10\"\n            AUTH_RATE_LIMIT_SIGNUPS_INTERVAL: 5m\n            AUTH_RATE_LIMIT_SMS_BURST: \"10\"\n            AUTH_RATE_LIMIT_SMS_INTERVAL: 1h\n            AUTH_REFRESH_TOKEN_EXPIRES_IN: \"2592000\"\n            AUTH_REQUIRE_ELEVATED_CLAIM: disabled\n            AUTH_SERVER_URL: http://${AUTH_URL}/v1\n            AUTH_SMTP_AUTH_METHOD: LOGIN\n            AUTH_SMTP_HOST: mailhog\n            AUTH_SMTP_PASS: password\n            AUTH_SMTP_PORT: \"1025\"\n            AUTH_SMTP_SECURE: \"false\"\n            AUTH_SMTP_SENDER: auth@example.com\n            AUTH_SMTP_USER: user\n            AUTH_TURNSTILE_SECRET: \"\"\n            AUTH_USER_DEFAULT_ALLOWED_ROLES: user,me\n            AUTH_USER_DEFAULT_ROLE: user\n            HASURA_GRAPHQL_ADMIN_SECRET: ${GRAPHQL_ADMIN_SECRET}\n            HASURA_GRAPHQL_DATABASE_URL: postgres://postgres:${POSTGRES_PASSWORD}@postgres:5432/postgres\n            HASURA_GRAPHQL_GRAPHQL_URL: http://graphql:8080/v1/graphql\n            HASURA_GRAPHQL_JWT_SECRET: ${JWT_SECRET}\n            POSTGRES_MIGRATIONS_CONNECTION: postgres://postgres:${POSTGRES_PASSWORD}@postgres:5432/postgres\n        extra_hosts:\n            - host.docker.internal:host-gateway\n            - ${AUTH_URL}:host-gateway\n            - ${DB_URL}:host-gateway\n            - ${FUNCTIONS_URL}:host-gateway\n            - ${GRAPHQL_URL}:host-gateway\n            - ${STORAGE_URL}:host-gateway\n        healthcheck:\n            test:\n                - CMD\n                - wget\n                - --spider\n                - -S\n                - http://localhost:4000/healthz\n            timeout: 60s\n            interval: 5s\n            start_period: 60s\n        labels:\n            traefik.enable: \"true\"\n            traefik.http.routers.auth.entrypoints: web\n            traefik.http.routers.auth.rule: Host(`${AUTH_URL}`)\n            traefik.http.routers.auth.service: auth\n            traefik.http.routers.auth.tls: \"false\"\n            traefik.http.services.auth.loadbalancer.server.port: \"4000\"\n        restart: always\n        volumes:\n            - type: bind\n              source: nhost/emails\n              target: /app/email-templates\n              read_only: false\n\n    console:\n        image: nhost/graphql-engine:v2.46.0-ce.cli-migrations-v3\n        depends_on:\n            graphql:\n                condition: service_healthy\n        entrypoint:\n            - hasura-cli\n        command:\n          - console\n          - --no-browser\n          - --endpoint=http://graphql:8080\n          - --address=0.0.0.0\n          - --console-port=9695\n          - --api-port=80\n          - --api-host=http://${GRAPHQL_URL}\n          - --console-hge-endpoint=http://${GRAPHQL_URL}\n        environment:\n            GRAPHITE_WEBHOOK_SECRET: \"\"\n            HASURA_GRAPHQL_ADMIN_INTERNAL_ERRORS: \"true\"\n            HASURA_GRAPHQL_ADMIN_SECRET: ${GRAPHQL_ADMIN_SECRET}\n            HASURA_GRAPHQL_CONSOLE_ASSETS_DIR: /srv/console-assets\n            HASURA_GRAPHQL_CORS_DOMAIN: '*'\n            HASURA_GRAPHQL_DATABASE_URL: postgres://postgres:${POSTGRES_PASSWORD}@postgres:5432/postgres\n            HASURA_GRAPHQL_DEV_MODE: \"true\"\n            HASURA_GRAPHQL_DISABLE_CORS: \"false\"\n            HASURA_GRAPHQL_ENABLE_ALLOWLIST: \"false\"\n            HASURA_GRAPHQL_ENABLE_CONSOLE: \"true\"\n            HASURA_GRAPHQL_ENABLE_REMOTE_SCHEMA_PERMISSIONS: \"false\"\n            HASURA_GRAPHQL_ENABLE_TELEMETRY: \"false\"\n            HASURA_GRAPHQL_ENABLED_APIS: metadata,graphql,pgdump,config\n            HASURA_GRAPHQL_ENABLED_LOG_TYPES: startup,http-log,webhook-log,websocket-log\n            HASURA_GRAPHQL_EVENTS_HTTP_POOL_SIZE: \"100\"\n            HASURA_GRAPHQL_INFER_FUNCTION_PERMISSIONS: \"true\"\n            HASURA_GRAPHQL_JWT_SECRET: ${JWT_SECRET}\n            HASURA_GRAPHQL_LIVE_QUERIES_MULTIPLEXED_BATCH_SIZE: \"100\"\n            HASURA_GRAPHQL_LIVE_QUERIES_MULTIPLEXED_REFETCH_INTERVAL: \"1000\"\n            HASURA_GRAPHQL_LOG_LEVEL: warn\n            HASURA_GRAPHQL_PG_CONNECTIONS: \"50\"\n            HASURA_GRAPHQL_PG_TIMEOUT: \"180\"\n            HASURA_GRAPHQL_STRINGIFY_NUMERIC_TYPES: \"false\"\n            HASURA_GRAPHQL_TX_ISOLATION: read-committed\n            HASURA_GRAPHQL_UNAUTHORIZED_ROLE: public\n            HASURA_GRAPHQL_USE_PREPARED_STATEMENTS: \"true\"\n            HASURA_GRAPHQL_WS_READ_COOKIE: \"false\"\n        extra_hosts:\n            - host.docker.internal:host-gateway\n            - ${AUTH_URL}:host-gateway\n            - ${DB_URL}:host-gateway\n            - ${FUNCTIONS_URL}:host-gateway\n            - ${GRAPHQL_URL}:0.0.0.0\n            - ${STORAGE_URL}:host-gateway\n        healthcheck:\n            test:\n                - CMD-SHELL\n                - timeout 1s bash -c ':> /dev/tcp/127.0.0.1/9695' || exit 1\n            timeout: 60s\n            interval: 5s\n            start_period: 60s\n        labels:\n            traefik.enable: \"true\"\n            traefik.http.routers.console.entrypoints: web\n            traefik.http.routers.console.rule: Host(`${GRAPHQL_URL}`)\n            traefik.http.routers.console.service: console\n            traefik.http.routers.console.tls: \"false\"\n            traefik.http.routers.migrate.entrypoints: web\n            traefik.http.routers.migrate.rule: Host(`${GRAPHQL_URL}`) && PathPrefix(`/apis/`)\n            traefik.http.routers.migrate.service: migrate\n            traefik.http.routers.migrate.tls: \"false\"\n            traefik.http.services.console.loadbalancer.server.port: \"9695\"\n            traefik.http.services.migrate.loadbalancer.server.port: \"80\"\n        restart: always\n        volumes:\n            - type: bind\n              source: nhost\n              target: /app\n              read_only: false\n        working_dir: /app\n\n    dashboard:\n        image: nhost/dashboard:2.34.0\n        environment:\n            NEXT_PUBLIC_ENV: dev\n            NEXT_PUBLIC_NHOST_ADMIN_SECRET: ${GRAPHQL_ADMIN_SECRET}\n            NEXT_PUBLIC_NHOST_AUTH_URL: http://${AUTH_URL}/v1\n            NEXT_PUBLIC_NHOST_FUNCTIONS_URL: http://${FUNCTIONS_URL}/v1\n            NEXT_PUBLIC_NHOST_GRAPHQL_URL: http://${GRAPHQL_URL}/v1/graphql\n            NEXT_PUBLIC_NHOST_HASURA_API_URL: http://${GRAPHQL_URL}\n            NEXT_PUBLIC_NHOST_HASURA_CONSOLE_URL: http://${GRAPHQL_URL}/console\n            NEXT_PUBLIC_NHOST_HASURA_MIGRATIONS_API_URL: http://${GRAPHQL_URL}\n            NEXT_PUBLIC_NHOST_PLATFORM: \"false\"\n            NEXT_PUBLIC_NHOST_STORAGE_URL: http://${STORAGE_URL}/v1\n        extra_hosts:\n            - host.docker.internal:host-gateway\n            - ${AUTH_URL}:host-gateway\n            - ${DB_URL}:host-gateway\n            - ${FUNCTIONS_URL}:host-gateway\n            - ${GRAPHQL_URL}:host-gateway\n            - ${STORAGE_URL}:host-gateway\n        labels:\n            traefik.enable: \"true\"\n            traefik.http.routers.dashboard.entrypoints: web\n            traefik.http.routers.dashboard.rule: Host(`${DASHBOARD_URL}`)\n            traefik.http.routers.dashboard.service: dashboard\n            traefik.http.routers.dashboard.tls: \"false\"\n            traefik.http.services.dashboard.loadbalancer.server.port: \"3000\"\n        restart: \"\"\n        working_dir: \"\"\n\n    functions:\n        image: nhost/functions:22-1.4.0\n        environment:\n            GRAPHITE_WEBHOOK_SECRET: \"\"\n            HASURA_GRAPHQL_ADMIN_SECRET: ${GRAPHQL_ADMIN_SECRET}\n            HASURA_GRAPHQL_DATABASE_URL: postgres://postgres:${POSTGRES_PASSWORD}@postgres:5432/postgres\n            HASURA_GRAPHQL_GRAPHQL_URL: http://graphql:8080/v1/graphql\n            HASURA_GRAPHQL_JWT_SECRET: ${JWT_SECRET}\n        extra_hosts:\n            - host.docker.internal:host-gateway\n            - ${AUTH_URL}:host-gateway\n            - ${DB_URL}:host-gateway\n            - ${FUNCTIONS_URL}:host-gateway\n            - ${GRAPHQL_URL}:host-gateway\n            - ${STORAGE_URL}:host-gateway\n        healthcheck:\n            test:\n                - CMD\n                - wget\n                - --spider\n                - -S\n                - http://localhost:3000/healthz\n            timeout: 600s\n            interval: 5s\n            start_period: 600s\n        labels:\n            traefik.enable: \"true\"\n            traefik.http.middlewares.replace-functions.replacepathregex.regex: /v1(/|$$)(.*)\n            traefik.http.middlewares.replace-functions.replacepathregex.replacement: /$$2\n            traefik.http.routers.functions.entrypoints: web\n            traefik.http.routers.functions.middlewares: replace-functions\n            traefik.http.routers.functions.rule: Host(`${FUNCTIONS_URL}`) && PathPrefix(`/v1`)\n            traefik.http.routers.functions.service: functions\n            traefik.http.routers.functions.tls: \"false\"\n            traefik.http.services.functions.loadbalancer.server.port: \"3000\"\n        restart: always\n        volumes:\n            - type: bind\n              source: .\n              target: /opt/project\n              read_only: false\n            - type: volume\n              source: root_node_modules\n              target: /opt/project/node_modules\n              read_only: false\n            - type: volume\n              source: functions_node_modules\n              target: /opt/project/functions/node_modules\n              read_only: false\n\n    graphql:\n        image: nhost/graphql-engine:v2.46.0-ce\n        depends_on:\n            postgres:\n                condition: service_healthy\n        environment:\n            GRAPHITE_WEBHOOK_SECRET: \"\"\n            HASURA_GRAPHQL_ADMIN_INTERNAL_ERRORS: \"true\"\n            HASURA_GRAPHQL_ADMIN_SECRET: ${GRAPHQL_ADMIN_SECRET}\n            HASURA_GRAPHQL_CONSOLE_ASSETS_DIR: /srv/console-assets\n            HASURA_GRAPHQL_CORS_DOMAIN: '*'\n            HASURA_GRAPHQL_DATABASE_URL: postgres://postgres:${POSTGRES_PASSWORD}@postgres:5432/postgres\n            HASURA_GRAPHQL_DEV_MODE: \"true\"\n            HASURA_GRAPHQL_DISABLE_CORS: \"false\"\n            HASURA_GRAPHQL_ENABLE_ALLOWLIST: \"false\"\n            HASURA_GRAPHQL_ENABLE_CONSOLE: \"true\"\n            HASURA_GRAPHQL_ENABLE_REMOTE_SCHEMA_PERMISSIONS: \"false\"\n            HASURA_GRAPHQL_ENABLE_TELEMETRY: \"false\"\n            HASURA_GRAPHQL_ENABLED_APIS: metadata,graphql,pgdump,config\n            HASURA_GRAPHQL_ENABLED_LOG_TYPES: startup,http-log,webhook-log,websocket-log\n            HASURA_GRAPHQL_EVENTS_HTTP_POOL_SIZE: \"100\"\n            HASURA_GRAPHQL_INFER_FUNCTION_PERMISSIONS: \"true\"\n            HASURA_GRAPHQL_JWT_SECRET: ${JWT_SECRET}\n            HASURA_GRAPHQL_LIVE_QUERIES_MULTIPLEXED_BATCH_SIZE: \"100\"\n            HASURA_GRAPHQL_LIVE_QUERIES_MULTIPLEXED_REFETCH_INTERVAL: \"1000\"\n            HASURA_GRAPHQL_LOG_LEVEL: warn\n            HASURA_GRAPHQL_PG_CONNECTIONS: \"50\"\n            HASURA_GRAPHQL_PG_TIMEOUT: \"180\"\n            HASURA_GRAPHQL_STRINGIFY_NUMERIC_TYPES: \"false\"\n            HASURA_GRAPHQL_TX_ISOLATION: read-committed\n            HASURA_GRAPHQL_UNAUTHORIZED_ROLE: public\n            HASURA_GRAPHQL_USE_PREPARED_STATEMENTS: \"true\"\n            HASURA_GRAPHQL_WS_READ_COOKIE: \"false\"\n        extra_hosts:\n            - host.docker.internal:host-gateway\n            - ${AUTH_URL}:host-gateway\n            - ${DB_URL}:host-gateway\n            - ${FUNCTIONS_URL}:host-gateway\n            - ${GRAPHQL_URL}:host-gateway\n            - ${STORAGE_URL}:host-gateway\n        healthcheck:\n            test:\n                - CMD-SHELL\n                - curl http://localhost:8080/healthz > /dev/null 2>&1\n            timeout: 60s\n            interval: 5s\n            start_period: 60s\n        labels:\n            traefik.enable: \"true\"\n            traefik.http.middlewares.replace-graphql.replacepathregex.regex: /v1(/|$$)(.*)\n            traefik.http.middlewares.replace-graphql.replacepathregex.replacement: /v1/graphql$$2\n            traefik.http.routers.hasura.entrypoints: web\n            traefik.http.routers.hasura.rule: Host(`${GRAPHQL_URL}`) && ( PathPrefix(`/v1`) || PathPrefix(`/v2`) || PathPrefix(`/api/`) || PathPrefix(`/console/assets`) )\n            traefik.http.routers.hasura.service: hasura\n            traefik.http.routers.hasura.tls: \"false\"\n            traefik.http.services.graphql.loadbalancer.server.port: \"8080\"\n            traefik.http.services.hasura.loadbalancer.server.port: \"8080\"\n        restart: always\n\n    mailhog:\n        image: jcalonso/mailhog:v1.0.1\n        environment:\n            SMTP_HOST: mailhog\n            SMTP_PASS: password\n            SMTP_PORT: \"1025\"\n            SMTP_SECURE: \"false\"\n            SMTP_SENDER: auth@example.com\n            SMTP_USER: user\n        extra_hosts:\n            - host.docker.internal:host-gateway\n            - ${AUTH_URL}:host-gateway\n            - ${DB_URL}:host-gateway\n            - ${FUNCTIONS_URL}:host-gateway\n            - ${GRAPHQL_URL}:host-gateway\n            - ${STORAGE_URL}:host-gateway\n        labels:\n            traefik.enable: \"true\"\n            traefik.http.routers.mailhog.entrypoints: web\n            traefik.http.routers.mailhog.rule: Host(`${MAILHOG_URL}`)\n            traefik.http.routers.mailhog.service: mailhog\n            traefik.http.routers.mailhog.tls: \"false\"\n            traefik.http.services.mailhog.loadbalancer.server.port: \"8025\"\n        restart: always\n        volumes:\n            - type: volume\n              source: mailhog\n              target: /maildir\n\n    minio:\n        image: minio/minio:RELEASE.2025-02-28T09-55-16Z\n        entrypoint:\n            - /bin/sh\n        command:\n            - -c\n            - mkdir -p /data/nhost && /usr/bin/minio server --address :9000 /data\n        environment:\n            MINIO_ROOT_USER: ${STORAGE_ACCESS_KEY}\n            MINIO_ROOT_PASSWORD: ${STORAGE_SECRET_KEY}\n        extra_hosts:\n            - host.docker.internal:host-gateway\n            - ${AUTH_URL}:host-gateway\n            - ${DB_URL}:host-gateway\n            - ${FUNCTIONS_URL}:host-gateway\n            - ${GRAPHQL_URL}:host-gateway\n            - ${STORAGE_URL}:host-gateway\n        restart: always\n        volumes:\n            - type: volume\n              source: minio\n              target: /data\n\n    postgres:\n        image: postgres:16\n        environment:\n            POSTGRES_DB: postgres\n            POSTGRES_DEV_INSECURE: \"true\"\n            POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}\n            POSTGRES_USER: postgres\n        extra_hosts:\n            - host.docker.internal:host-gateway\n            - ${AUTH_URL}:host-gateway\n            - ${DB_URL}:host-gateway\n            - ${FUNCTIONS_URL}:host-gateway\n            - ${GRAPHQL_URL}:host-gateway\n            - ${STORAGE_URL}:host-gateway\n        healthcheck:\n            test:\n                - CMD-SHELL\n                - pg_isready -U postgres\n                - -d\n                - postgres\n                - -q\n            timeout: 60s\n            interval: 5s\n            start_period: 60s\n        ports:\n            - mode: ingress\n              target: 5432\n              published: \"5432\"\n              protocol: tcp\n        restart: always\n        volumes:\n            - type: volume\n              source: pgdata\n              target: /var/lib/postgresql/data/\n              read_only: false\n            - type: bind\n              source: ./initdb.d\n              target: /docker-entrypoint-initdb.d\n              read_only: true\n\n    storage:\n        image: nhost/storage:0.7.2\n        depends_on:\n            graphql:\n                condition: service_healthy\n            minio:\n                condition: service_started\n            postgres:\n                condition: service_healthy\n        command:\n            - serve\n        environment:\n            BIND: :5000\n            HASURA_ENDPOINT: http://graphql:8080/v1\n            HASURA_GRAPHQL_ADMIN_SECRET: ${GRAPHQL_ADMIN_SECRET}\n            HASURA_METADATA: \"1\"\n            POSTGRES_MIGRATIONS: \"1\"\n            POSTGRES_MIGRATIONS_SOURCE: postgres://postgres:${POSTGRES_PASSWORD}@postgres:5432/postgres?sslmode=disable\n            PUBLIC_URL: http://${STORAGE_URL}\n            S3_ACCESS_KEY: ${STORAGE_ACCESS_KEY}\n            S3_BUCKET: nhost\n            S3_ENDPOINT: http://minio:9000\n            S3_REGION: \"\"\n            S3_ROOT_FOLDER: \"\"\n            S3_SECRET_KEY: ${STORAGE_SECRET_KEY}\n        extra_hosts:\n            - host.docker.internal:host-gateway\n            - ${AUTH_URL}:host-gateway\n            - ${DB_URL}:host-gateway\n            - ${FUNCTIONS_URL}:host-gateway\n            - ${GRAPHQL_URL}:host-gateway\n            - ${STORAGE_URL}:host-gateway\n        labels:\n            traefik.enable: \"true\"\n            traefik.http.routers.storage.entrypoints: web\n            traefik.http.routers.storage.rule: Host(`${STORAGE_URL}`) && PathPrefix(`/v1`)\n            traefik.http.routers.storage.service: storage\n            traefik.http.routers.storage.tls: \"false\"\n            traefik.http.services.storage.loadbalancer.server.port: \"5000\"\n        restart: always\n\n    traefik:\n        image: traefik:v3.1\n        command:\n            - --api.insecure=true\n            - --providers.docker=true\n            - --providers.file.directory=/opt/traefik\n            - --providers.file.watch=true\n            - --providers.docker.exposedbydefault=false\n            - --providers.docker.constraints=Label(`com.docker.compose.project`,`docker-compose`)\n            - --entrypoints.web.address=:80\n        extra_hosts:\n            - host.docker.internal:host-gateway\n            - ${AUTH_URL}:host-gateway\n            - ${DB_URL}:host-gateway\n            - ${FUNCTIONS_URL}:host-gateway\n            - ${GRAPHQL_URL}:host-gateway\n            - ${STORAGE_URL}:host-gateway\n        ports:\n            - mode: ingress\n              target: 80\n              published: \"80\"\n              protocol: tcp\n        restart: always\n        volumes:\n            - type: bind\n              source: /var/run/docker.sock\n              target: /var/run/docker.sock\n              read_only: true\n\nvolumes:\n    functions_node_modules: {}\n    root_node_modules: {}\n    pgdata: {}\n    mailhog: {}\n    minio: {}\n",
      "source_url": "https://github.com/nhost/nhost/blob/e5845916dcec56e1dc22b19be45ef01e954416e2/examples/docker-compose/docker-compose.yaml",
      "validation_status": "config_ok"
    },
    {
      "order": 5,
      "kind": "shell",
      "language": "bash",
      "label": "Copy .env.example to .env",
      "body": "cp .env.example .env",
      "source_url": "https://github.com/nhost/nhost/blob/e5845916dcec56e1dc22b19be45ef01e954416e2/examples/docker-compose/README.md",
      "validation_status": "unvalidated"
    },
    {
      "order": 6,
      "kind": "note",
      "language": "text",
      "label": "What the compose file check ran with",
      "body": "The compose file check ran in a checkout of nhost/nhost at e5845916dcec56e1dc22b19be45ef01e954416e2 (a sparse checkout: the top-level files and the folder examples/docker-compose), in the folder examples/docker-compose, after .env.example was copied to .env. The example guide tells the reader to clone the repository, go into examples/docker-compose, copy .env.example to .env and run docker compose up -d.",
      "source_url": "https://github.com/nhost/nhost/blob/e5845916dcec56e1dc22b19be45ef01e954416e2/examples/docker-compose/README.md",
      "validation_status": "unvalidated"
    },
    {
      "order": 7,
      "kind": "shell",
      "language": "bash",
      "label": "Start it",
      "body": "docker compose up -d",
      "source_url": "https://github.com/nhost/nhost/blob/e5845916dcec56e1dc22b19be45ef01e954416e2/examples/docker-compose/README.md",
      "validation_status": "unvalidated"
    },
    {
      "order": 8,
      "kind": "verify",
      "language": "text",
      "label": "Open the app",
      "body": "Open http://local.dashboard.local.nhost.run. The docs name this address. The compose file publishes host port 80 for the traefik service.",
      "source_url": "https://github.com/nhost/nhost/blob/e5845916dcec56e1dc22b19be45ef01e954416e2/examples/docker-compose/README.md",
      "validation_status": "unvalidated"
    }
  ],
  "notes": [
    {
      "kind": "gotcha",
      "title": "The compose file has placeholder values",
      "body": "Placeholder values: AUTH_SMTP_PASS is \"password\" (auth), SMTP_PASS is \"password\" (mailhog).",
      "source_url": "https://github.com/nhost/nhost/blob/e5845916dcec56e1dc22b19be45ef01e954416e2/examples/docker-compose/docker-compose.yaml",
      "checked_at": "2026-10-22"
    },
    {
      "kind": "gotcha",
      "title": "The compose file publishes host ports",
      "body": "Host ports: 5432 (postgres), 80 (traefik). A port without a host address listens on all host network interfaces.",
      "source_url": "https://github.com/nhost/nhost/blob/e5845916dcec56e1dc22b19be45ef01e954416e2/examples/docker-compose/docker-compose.yaml",
      "checked_at": "2026-10-22"
    },
    {
      "kind": "gotcha",
      "title": "The compose file mounts host folders",
      "body": "Host paths mounted into containers: nhost/emails at /app/email-templates (auth), nhost at /app (console), . at /opt/project (functions), ./initdb.d at /docker-entrypoint-initdb.d (postgres).",
      "source_url": "https://github.com/nhost/nhost/blob/e5845916dcec56e1dc22b19be45ef01e954416e2/examples/docker-compose/docker-compose.yaml",
      "checked_at": "2026-10-22"
    },
    {
      "kind": "gotcha",
      "title": "Some services get elevated host access",
      "body": "traefik mounts the Docker socket, which gives it control of Docker on the host.",
      "source_url": "https://github.com/nhost/nhost/blob/e5845916dcec56e1dc22b19be45ef01e954416e2/examples/docker-compose/docker-compose.yaml",
      "checked_at": "2026-10-22"
    }
  ],
  "signals": {
    "captured_at": "2026-10-22",
    "stars": 9364,
    "forks": 636,
    "open_issues": 155,
    "last_push": "2026-10-22",
    "days_since_push": 0,
    "latest_release": "@nhost/dashboard@3.8.0",
    "latest_release_date": "2026-10-20",
    "days_since_release": 2
  },
  "links": {
    "html": "https://openlogs.fyi/tools/nhost",
    "markdown": "https://openlogs.fyi/tools/nhost.md",
    "json": "https://openlogs.fyi/tools/nhost.json"
  },
  "updated_at": "2026-10-22"
}
