Skip to content

Self-host OpenGMB with Docker Compose

To run the OpenGMB community edition with Docker Compose, copy the example settings file, fill in the required values and the owner login, start the stack, sign in, and connect your own Google Cloud project. This guide lists every setting, then covers the first run, backups and upgrades. It assumes Docker Engine 24 or newer and a public HTTPS hostname.

Last updated 2026-10-25

Get started

How do I configure it?

Clone the repository from github.com/synectus/OpenGMB, then work in the folder below.

Terminal
cd deploy/selfhost
cp .env.example .env

Generate the secrets and paste them into the file:

Secrets
openssl rand -hex 24      # POSTGRES_PASSWORD
openssl rand -base64 32   # BETTER_AUTH_SECRET
openssl rand -base64 32   # ENCRYPTION_KEY
openssl rand -base64 32   # AUDIT_CHECKPOINT_KEY

The file is git-ignored. Never commit it. These are all the settings it holds:

Owner login

OWNER_EMAIL

The email you sign in with. On the first start, when the database has no organization, OpenGMB creates the organization and this owner. There is no sign-up page.

OWNER_PASSWORD

The first password, 8 to 128 characters. Change it in the app after you sign in. From then on this value is ignored, so you can remove it from the file.

OWNER_ORG_NAME

Optional. The name of the first organization. It starts as My agency.

Required settings

POSTGRES_PASSWORD

The password for the bundled database. Use letters and digits only, because it goes inside a connection URL.

WEB_URL

The public URL of the web app, with no trailing slash. It is also the base for the Google sign-in redirect.

BETTER_AUTH_SECRET

Signs sessions. A random string.

ENCRYPTION_KEY

Encrypts stored secrets such as Google refresh tokens and DataForSEO keys. Exactly 32 bytes, base64. Keep a copy: if it is lost or changed, those secrets become unreadable.

AUDIT_CHECKPOINT_KEY

Signs the audit log checkpoints. A different value from the encryption key.

WORKER_PUBLIC_URL

The public address DataForSEO calls back on. Point it at your web address. Instant review alerts need no worker address, because Google Pub/Sub pushes to the web app.

Host ports

WEB_HOST_PORT

The port the web app is published on. It starts at 8080. The app listens on 3000 inside its container.

MCP_HOST_PORT

The port the MCP server is published on. It starts at 8081. It listens on 3100 inside its container.

WORKER_HOST_PORT

The port the worker is published on. It starts at 8082. It listens on 3200 inside its container.

Google

GOOGLE_CLIENT_ID

The OAuth client ID from your own Google Cloud project. Add two authorized redirect URIs to that client: your web address followed by /api/google/callback (connects Google Business Profile) and your web address followed by /api/auth/callback/google (Sign in with Google).

GOOGLE_CLIENT_SECRET

The matching OAuth client secret.

GOOGLE_BUSINESS_API_MODE

Either mock or live. It starts as mock, which makes no live Google calls. Switch to live only after Google approves Business Profile API access for your project.

GOOGLE_MAPS_BROWSER_KEY

Optional. A Maps JavaScript API key for the rank map, restricted by referrer to your web address. Without it the page shows a placeholder and the table.

PUBSUB_TOPIC

Optional. The full name of your Pub/Sub topic, projects/<project>/topics/<topic>. OpenGMB tells each connected Google account to send review notifications to it. Instant review alerts need this and the next two settings.

PUBSUB_AUDIENCE

Optional. The audience set on your Pub/Sub push subscription. Use your web address followed by /webhooks/google-pubsub. Empty means the notification webhook answers 503 and nothing else breaks.

PUBSUB_SERVICE_ACCOUNT_EMAIL

Optional. The service account the push subscription signs as.

AI, rank tracking and images

ANTHROPIC_API_KEY

Optional. Your own Anthropic key. It switches on AI drafts, review reply suggestions and report summaries, billed to you by Anthropic.

DATAFORSEO_LOGIN

Optional. Your DataForSEO API login, for rank tracking. It is billed to you by DataForSEO at their price.

DATAFORSEO_PASSWORD

The DataForSEO API password. It is generated in their dashboard and is not your account password.

PIXABAY_API_KEY

Optional. A Pixabay key for stock photos on posts.

OPENAI_API_KEY

Optional. An OpenAI key for AI-generated post images.

Email

RESEND_API_KEY

Optional. Your Resend key. Empty means the daily digest is skipped.

EMAIL_FROM

The sender address, on a domain you verified in Resend.

Push alerts

VAPID_PUBLIC_KEY

Optional. Alerts on installed browsers need all three push settings. Leave all three empty to hide the feature.

VAPID_PRIVATE_KEY

The private half of the pair. Generate the pair once with npx web-push generate-vapid-keys --json and never change it after people subscribe.

VAPID_SUBJECT

A mailto: or https: contact address.

How do I start it?

Start the stack
docker compose up -d --build
docker compose ps
docker compose logs migrate

The first command builds one image, runs the migrations, then starts the web app, the MCP server and the worker. After it, postgres, web, mcp and worker should report healthy, and migrate should have exited with code 0.

What do I do on the first run?

  1. Open your web address and sign in with the owner email and password from your settings file. The first start created the organization and this owner. Change the password in the app. After that the password in the file is ignored.
  2. Set up two-factor sign-in for the owner login when the app asks.
  3. Open Settings, then Google connection, and connect Google. This needs the OAuth client from your Google Cloud project, with both redirect URIs added: your web address followed by /api/google/callback and your web address followed by /api/auth/callback/google.
  4. Write the audit log checkpoint for the organization that now exists. It refuses to run on an empty database, so do it after the first start has created the owner.
Audit checkpoint
docker compose run --rm --no-deps mcp pnpm audit:checkpoint-backfill

To connect an AI agent, create an API token in the app under Settings, then API tokens, or from the command line:

Create a token
docker compose run --rm --no-deps mcp pnpm token:create you@example.com "my laptop"

The command prints a claude mcp add line that uses the container port. Replace the address with your public one, which is your web address followed by /mcp. The MCP guide has the rest.

How do I turn on instant review alerts?

Without this, new reviews arrive when someone presses Sync. With it, Google pushes each new or edited review to your web app at /webhooks/google-pubsub within seconds. It runs in your own Google Cloud project. Do these steps once:

  1. In the Google Cloud console, enable the My Business Notifications API for the same project as your OAuth client (APIs and Services, Library). If it is off, registering a Google account fails with a message naming this API.
  2. Create a Pub/Sub topic (Pub/Sub, Topics). Copy its full name, which looks like projects/my-project/topics/opengmb-reviews.
  3. On that topic, add mybusiness-api-pubsub@system.gserviceaccount.com as a principal with the Pub/Sub Publisher role (roles/pubsub.publisher). This is Google's own account for Business Profile notifications. If your organization blocks outside accounts, an organization policy exception may be needed.
  4. Create a service account for delivery, then a push subscription on the topic. Set the endpoint to your web address followed by /webhooks/google-pubsub, turn on authentication with that service account, and set the audience to the same endpoint URL.
  5. Put the topic name, the audience and the service account email in the settings file as PUBSUB_TOPIC, PUBSUB_AUDIENCE and PUBSUB_SERVICE_ACCOUNT_EMAIL, then restart the stack.
  6. Connect Google, or reconnect it. New Google connections are registered automatically, and a daily check repairs any account that drifted.

The status page shows Instant review alerts as Live once the three settings are saved, at least one account is registered and a notification has arrived in the last 30 days. Until the first one arrives it reads Waiting for first notification.

How do I back it up?

All state is in the Postgres volume, including the job queue. Dump it from cron and copy the dump off the machine.

Backup
docker compose exec -T postgres sh -c 'pg_dump -U "$POSTGRES_USER" -d "$POSTGRES_DB" --format=custom' > backup.dump

To restore, start only the database and load the dump into it:

Restore
docker compose up -d postgres
docker compose exec -T postgres sh -c 'pg_restore -U "$POSTGRES_USER" -d "$POSTGRES_DB" --clean --if-exists' < backup.dump

How do I upgrade?

Take a backup first. Migrations run when the stack starts and only move forward. To roll back, restore the dump and check out the older version.

Upgrade
git pull
docker compose up -d --build
docker compose logs migrate

What if something goes wrong?

  • Run docker compose config to print the resolved configuration. A missing required value is reported by name.
  • Run docker compose logs -f web mcp worker for application logs. The worker prints which Pub/Sub settings are missing.
  • Sign-in sends you to the wrong host: the web address setting must be the exact public URL, including https://.
  • Google says redirect_uri_mismatch: the OAuth client needs two authorized redirect URIs, each exactly your web address followed by /api/google/callback (connect Google Business Profile) and /api/auth/callback/google (Sign in with Google).

Where next?