meerko

Get started

Self-host Meerko

Run the whole of Meerko yourself: the API, the background worker that sends and reads email, the web dashboard and Postgres. It's the same code as Meerko Cloud, under AGPL-3.0. Credits are off, so lead search and enrichment run on your own provider key.

Docs menu

What you'll run

PieceWhat it does
PostgresAll your data. docker-compose.yml runs Postgres 17 on port 54329.
API · :8787Everything the CLI, agents and the dashboard call, including the MCP server at /mcp. Also signs apps in (OAuth) and serves unsubscribe links.
WorkerSends due emails every 30 seconds (at most one per mailbox per pass) and scans for replies every 2 minutes.
Web · :3000The dashboard: leads, sequences, the inbox and settings.

You need Node 20 or later, pnpm and Docker.

1Install

bash
$ git clone https://github.com/iker-gonzalez/meerko.ai ~/meerko
$ cd ~/meerko
$ pnpm install
$ cp .env.example .env

.env works as-is for a local install. You'll fill in the Google and search keys in steps 4 and 5.

2Start the database

bash
$ pnpm db:up # Postgres on localhost:54329
$ pnpm db:migrate
$ pnpm db:seed # prints your API key (mk_…)

The seed creates a workspace and an API key named "seed", and prints the key once. Keep it: the CLI, your agent and the dashboard can all log in with it.

3Run Meerko

bash
$ pnpm dev

This starts the API on http://localhost:8787, the worker and the dashboard on http://localhost:3000. Open localhost:3000/login and log in with the API key from the seed. To run them one at a time, use pnpm --filter @meerko/api dev, @meerko/worker or @meerko/web.

Until you connect Google, the worker logs "Sender idle" and sends nothing. Tables, imports and AI columns already work.

4Connect Google (to send email)

Meerko sends from Gmail through Google's API, so it needs an OAuth client of your own. The same client also gives the dashboard "Sign in with Google".

  1. In a Google Cloud project, enable the Gmail API (Google's guide).
  2. Configure the OAuth consent screen and add the Google accounts you'll send from as test users.
  3. Create an OAuth client of type Web application with these authorized redirect URIs:
    • http://localhost:8787/oauth/google/callback (connect Gmail)
    • http://localhost:8787/auth/google/callback (sign in to the dashboard)
  4. Put its ID and secret in .env, plus an encryption key for the stored Gmail tokens:
.env
$ GOOGLE_CLIENT_ID=…apps.googleusercontent.com
$ GOOGLE_CLIENT_SECRET=…
# generate with: openssl rand -base64 32
$ MEERKO_ENCRYPTION_KEY=…

Restart pnpm dev, then connect a mailbox from Settings in the dashboard (or pnpm meerko mailboxes connect).

While the consent screen is in "Testing", Google expires the refresh token after 7 days (Google's docs). Meerko then marks the mailbox for reconnecting and pauses its sends until you do. Publish the consent screen to avoid it. Never change MEERKO_ENCRYPTION_KEY once mailboxes are connected: the stored tokens can't be read without it.

To sign in with Google instead of an API key, invite your address with pnpm db:invite you@example.com. With MEERKO_SIGNUP=open, any Google account can create a workspace.

Environment variables

All of them live in .env at the repo root; .env.example has comments for each.

VariableWhat it's for
DATABASE_URLPostgres connection string. Required.
PORTThe API's port. Default 8787.
MEERKO_PUBLIC_URLThe API's public URL, used for the Google redirects and unsubscribe links. Default http://localhost:8787.
MEERKO_APP_URLThe dashboard's URL. After connecting Gmail or signing in, the browser comes back here, and apps are approved on its consent page. Default http://localhost:3000.
MEERKO_API_URLWhere the dashboard, the CLI and the MCP server reach the API. Default http://localhost:8787.
MEERKO_ENCRYPTION_KEY32 bytes, base64. Encrypts Gmail tokens, provider keys and unsubscribe links. Required to send and for app sign-in.
GOOGLE_CLIENT_ID · GOOGLE_CLIENT_SECRETYour OAuth client. Required to send email and to sign in with Google.
MEERKO_SIGNUPinvite (default): only addresses added with pnpm db:invite can sign in with Google. open: anyone can.
ICYPEAS_API_KEYLead search and email finding for every workspace on this server.
MEERKO_CREDITSoff (default) when self-hosting. on meters search and enrichment in credits, as on Meerko Cloud.
MEERKO_TICK_SECRETMeerko Cloud only, where a scheduler replaces the worker. Leave it empty.

Connect your agent

The API serves the MCP server at http://localhost:8787/mcp. With Google connected (step 4), agents and the CLI sign in with the browser, as on Meerko Cloud:

bash
$ claude mcp add --transport http --scope user meerko http://localhost:8787/mcp
$ pnpm meerko login

Without Google, use the key from the seed: add --header "Authorization: Bearer mk_…" to the first command, and --key mk_… to the second. More in Claude Code and Codex & Cursor.

Running it in production

  • Serve the API on a public URL and set MEERKO_PUBLIC_URL to it: recipients click unsubscribe links on it, and Google redirects to it. Update the OAuth client's redirect URIs to match.
  • Build the API and dashboard images from the repo root with apps/api/Dockerfile and apps/web/Dockerfile. The dashboard takes NEXT_PUBLIC_SITE_URL at build time (same origin as MEERKO_APP_URL) and MEERKO_API_URL at runtime.
  • Keep a worker running: pnpm --filter @meerko/worker start. Running more than one at once is safe. On shutdown it finishes the current pass first.
  • Run pnpm db:migrate against the production database before each deploy; migrations don't run on start.
  • Back up Postgres, and the encryption key with it.

Updating

bash
$ git pull
$ pnpm install
$ pnpm db:migrate