Skip to content

Quickstart

Get Plume running locally and send your first test email in about five minutes.

You’ll need Docker and Docker Compose. You do not need an email provider to start — until you connect one, Plume uses a built-in log provider that writes emails to stdout instead of sending them, so it runs with zero setup. When you’re ready to send for real, connect Amazon SES (cheapest at volume) or any SMTP provider — Mailgun, SendGrid, Postmark, Resend, and more — in the app UI.

Clone the repo.

Terminal window
git clone https://github.com/plume-newsletter/plume.git
cd plume

Plume is configured entirely through environment variables — there is no .env.example and no config file. The bundled docker-compose.yml sets the required variables for you; the ones you’ll want to review are the first-admin bootstrap and the three secrets:

environment:
PLUME_DATABASE_URL: postgres://plume:plume@db:5432/plume?sslmode=disable
PLUME_COOKIE_SECRET: change-me-to-at-least-32-bytes-long
PLUME_SECRET_KEY: 32-byte-key-exactly-0123456789ab
PLUME_ADMIN_EMAIL: you@example.com
PLUME_ADMIN_PASSWORD: change-me
PLUME_BASE_URL: http://localhost:8080

PLUME_ADMIN_EMAIL / PLUME_ADMIN_PASSWORD bootstrap the first admin user on the initial boot. See Configuration for the full list.

Bring up Plume and PostgreSQL together. Migrations run automatically on every boot.

Terminal window
docker compose up -d
# → UI live at http://localhost:8080

In the dashboard, add a brand with your from-address. Plume shows the DNS records to verify it — add them at your provider and Plume confirms automatically.

With a draft campaign created, fire a test send. Create an API key under Settings → API keys, then call the campaign test endpoint. Pick your language:

Terminal window
# cURL
curl -X POST http://localhost:8080/api/campaigns/cmp_123/test \
-H "Authorization: Bearer plume_xxx" \
-H "Content-Type: application/json" \
-d '{"to":"you@example.com"}'
// JavaScript
await fetch("http://localhost:8080/api/campaigns/cmp_123/test", {
method: "POST",
headers: {
"Authorization": "Bearer plume_xxx",
"Content-Type": "application/json",
},
body: JSON.stringify({ to: "you@example.com" }),
});
// Go
req, _ := http.NewRequest("POST",
"http://localhost:8080/api/campaigns/cmp_123/test",
strings.NewReader(`{"to":"you@example.com"}`))
req.Header.Set("Authorization", "Bearer plume_xxx")
req.Header.Set("Content-Type", "application/json")
http.DefaultClient.Do(req)