Quickstart
Get Plume running locally and send your first test email in about five minutes.
Prerequisites
Section titled “Prerequisites”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.
1. Clone
Section titled “1. Clone”Clone the repo.
git clone https://github.com/plume-newsletter/plume.gitcd plume2. Configure the environment
Section titled “2. Configure the environment”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:8080PLUME_ADMIN_EMAIL / PLUME_ADMIN_PASSWORD bootstrap the first admin user on the initial boot. See Configuration for the full list.
3. Start the stack
Section titled “3. Start the stack”Bring up Plume and PostgreSQL together. Migrations run automatically on every boot.
docker compose up -d
# → UI live at http://localhost:80804. Create your brand
Section titled “4. Create your brand”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.
5. Send a test
Section titled “5. Send a test”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:
# cURLcurl -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"}'// JavaScriptawait 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" }),});// Goreq, _ := 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)