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
How do I configure it?
Clone the repository from github.com/synectus/OpenGMB, then work in the folder below.
cd deploy/selfhost cp .env.example .env
Generate the secrets and paste them into the file:
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_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.
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?
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?
- 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.
- Set up two-factor sign-in for the owner login when the app asks.
- 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.
- 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.
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:
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:
- 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.
- Create a Pub/Sub topic (Pub/Sub, Topics). Copy its full name, which looks like projects/my-project/topics/opengmb-reviews.
- 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.
- 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.
- 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.
- 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.
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:
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.
git pull docker compose up -d --build docker compose logs migrate
What if something goes wrong?
- Run
docker compose configto print the resolved configuration. A missing required value is reported by name. - Run
docker compose logs -f web mcp workerfor 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?
- Self-hosting overviewWhat you run, what you need and how Google access works.
- Connect an AI agentCreate a token and add the MCP server to Claude or Cursor.