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
| Piece | What it does |
|---|---|
| Postgres | All your data. docker-compose.yml runs Postgres 17 on port 54329. |
| API · :8787 | Everything the CLI, agents and the dashboard call, including the MCP server at /mcp. Also signs apps in (OAuth) and serves unsubscribe links. |
| Worker | Sends due emails every 30 seconds (at most one per mailbox per pass) and scans for replies every 2 minutes. |
| Web · :3000 | The dashboard: leads, sequences, the inbox and settings. |
You need Node 20 or later, pnpm and Docker.
1Install
$ 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
$ 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
$ 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".
- In a Google Cloud project, enable the Gmail API (Google's guide).
- Configure the OAuth consent screen and add the Google accounts you'll send from as test users.
- 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)
- Put its ID and secret in
.env, plus an encryption key for the stored Gmail tokens:
$ 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.
5Turn on lead search
People and company search and email finding run on Icypeas. Put your Icypeas API key in ICYPEAS_API_KEY for the whole server, or store one per workspace with pnpm meerko keys set icypeas <key> (needs MEERKO_ENCRYPTION_KEY). Without either, search and enrichment return an error; everything else works, including importing your own CSV.
Environment variables
All of them live in .env at the repo root; .env.example has comments for each.
| Variable | What it's for |
|---|---|
| DATABASE_URL | Postgres connection string. Required. |
| PORT | The API's port. Default 8787. |
| MEERKO_PUBLIC_URL | The API's public URL, used for the Google redirects and unsubscribe links. Default http://localhost:8787. |
| MEERKO_APP_URL | The 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_URL | Where the dashboard, the CLI and the MCP server reach the API. Default http://localhost:8787. |
| MEERKO_ENCRYPTION_KEY | 32 bytes, base64. Encrypts Gmail tokens, provider keys and unsubscribe links. Required to send and for app sign-in. |
| GOOGLE_CLIENT_ID · GOOGLE_CLIENT_SECRET | Your OAuth client. Required to send email and to sign in with Google. |
| MEERKO_SIGNUP | invite (default): only addresses added with pnpm db:invite can sign in with Google. open: anyone can. |
| ICYPEAS_API_KEY | Lead search and email finding for every workspace on this server. |
| MEERKO_CREDITS | off (default) when self-hosting. on meters search and enrichment in credits, as on Meerko Cloud. |
| MEERKO_TICK_SECRET | Meerko 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:
$ 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_URLto 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/Dockerfileandapps/web/Dockerfile. The dashboard takesNEXT_PUBLIC_SITE_URLat build time (same origin asMEERKO_APP_URL) andMEERKO_API_URLat 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:migrateagainst the production database before each deploy; migrations don't run on start. - Back up Postgres, and the encryption key with it.
Updating
$ git pull$ pnpm install$ pnpm db:migrate