Self-hosted service
savvyagents/larasend avatar
savvyagents/larasend

Larasend: a self-hosted transactional email control plane for Laravel teams

Self-hosted transactional email platform — send through your own AWS SES account, keep the dashboard, API keys, delivery logs, and webhooks on your own infrastructure.

497 stars61 forksPHPMIT

At a glance

What is it?
Larasend puts the dashboard, API keys, delivery logs and webhooks on your own server while Amazon SES or Cloudflare does the actual sending. It is a control plane, not a mail server, and the trade-offs sit exactly where you would expect.
Who is it for?
Adopt Larasend if you already run Laravel, want SES or Cloudflare as the sender, and need email logs, suppressions and API keys on infrastructure you control. Skip it if you want a hosted API with zero operations, or if you cannot run a queue worker and a scheduler.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 46 days ago.
What is it written in?
Mainly PHP, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The gap Larasend fills between Laravel Mail and your SES account

Most Laravel applications start by pointing the mail config at a provider and stop thinking about it. That works until three things happen at once: several applications send through the same account, someone asks which message bounced for which customer, and the answer lives in a provider dashboard that nobody on the team has access to. The usual fix is to wire each app directly to SES, which multiplies IAM credentials and leaves delivery history scattered.

Larasend inserts a control plane between the two. Applications talk to Larasend over an HTTP API or a Laravel mail transport, Larasend stores the MIME payload and metadata, and a queue worker hands the raw message to the configured provider. The sending account stays yours: the README describes sending through Amazon SES with your own verified domains, configuration sets and IAM controls, or through Cloudflare Email Service with a single scoped API token.

The intended user is a Laravel team that is comfortable running PostgreSQL, Redis and a container stack. The README lists PHP 8.4+ and Node.js 22+ for local development, and PostgreSQL 17+ with Redis 7+ as requirements. This is not a product for a solo developer who wants to paste an API key into a form and be done.

How a message travels from your app to the provider and back

The README lays out five steps, and they are worth reading carefully because they explain most of the operational constraints later.

Your Laravel app sends through the Larasend transport or directly through the HTTP API. Larasend validates the request, stores the MIME payload and metadata, and queues delivery. The queue worker sends the raw email through the source's provider, using the Amazon SES HTTPS API or authenticated SMTP against Cloudflare Email Service. SES then publishes delivery, bounce, complaint, open and click events back to Larasend through the SES webhook, and Larasend updates the activity dashboard, metrics, suppressions and webhook deliveries.

The Cloudflare path is different in a way that matters. Cloudflare has no event webhooks and no open or click tracking, so delivery state is recorded at send time and the suppression list syncs hourly from the Cloudflare account-level list. If you need bounce events in near real time, SES is the source that provides them; Cloudflare gives you a cheaper operational story and a coarser one.

One design decision deserves attention: Larasend accepts API sends even when provider quota sync is stale or temporarily unavailable, because the provider remains the final authority for send rejection. That is a reasonable choice for availability, but it means the dashboard's quota view can lag reality, and a send can be accepted by Larasend and rejected downstream.

Quick start with Docker Compose and the first API send

The README's quick start downloads two files into an empty directory and runs the bundled Compose stack. The app image runs migrations on boot, and the stack also starts a queue worker, a scheduler, PostgreSQL and Redis.

bash
mkdir larasend
cd larasend
curl -fsSL https://raw.githubusercontent.com/savvyagents/larasend/main/docker-compose.yml -o docker-compose.yml
curl -fsSL https://raw.githubusercontent.com/savvyagents/larasend/main/.env.example -o .env

Before starting anything, edit .env. The README shows the production values to set, including the database password and the database-backed session, cache and queue drivers.

env
APP_ENV=production
APP_DEBUG=false
APP_URL=https://mail.example.com
DB_PASSWORD=change-me
QUEUE_CONNECTION=database
SESSION_DRIVER=database
CACHE_STORE=database

Generate an application key with a one-off container run, then paste the printed base64 value into APP_KEY.

bash
docker compose pull
docker compose run --rm --entrypoint php app artisan key:generate --show

Start the stack and open APP_URL to create the first user and follow onboarding.

bash
docker compose up -d

The Compose file publishes the app on port 8080 by default through LARASEND_HTTP_PORT, and its health check polls http://127.0.0.1:8080/up. The queue service runs php artisan queue:work --queue=default,webhooks --tries=3 --timeout=150, and the scheduler runs php artisan schedule:work. Those two processes handle DNS verification, quota refresh, suppression sync and retention work, so a deployment without them will look healthy and quietly stop doing background jobs.

For a real send, create an API key in the dashboard and post to /api/emails with that key. The README documents the endpoint and API key authentication but does not print a full request body, so check the dashboard or the routes directory for the exact payload shape rather than guessing at field names.

The MIME disk, the control mailer, and two ways to lock yourself out

Two configuration decisions in Larasend can break an install in ways that are not obvious from the dashboard.

The first is LARASEND_MIME_DISK. The README is explicit that queued delivery, inbound attachments and email downloads all depend on the raw MIME content staying available to every web and worker instance, and that a deployment's ephemeral local filesystem must not be used for it. On Laravel Cloud that means setting FILESYSTEM_DISK=s3 and LARASEND_MIME_DISK=s3 with S3-compatible credentials. The bundled Compose file avoids the problem with a named volume shared by the app, queue and scheduler services, which is fine on a single host and not fine once you scale to multiple hosts without shared storage.

The second is LARASEND_CONTROL_MAILER. Authentication email is deliberately kept separate from the transactional provider. New-user verification, password recovery and new-member invitations only activate after an administrator sets LARASEND_CONTROL_MAILER to a tested, independently configured mailer such as smtp, ses, postmark or resend. The README states that log, array and larasend are rejected as control mailers so the instance is never its own only recovery path. Until that mailer is configured and tested, workspace owners see a dashboard warning and invitations that would create an unreachable account are blocked.

There is an escape hatch for the locked-out case, and it requires shell access: php artisan larasend:verify-user [email protected] --force verifies one exact account. That is a deliberate last resort, not a workflow.

Where Larasend is the wrong tool

Larasend is a control plane for outbound and, on Cloudflare sources, inbound mail. It is not an SMTP server and it does not deliver mail itself. If your requirement is to run your own MTA on your own IPs, this is a different category of software and Larasend will not help.

The provider dependency is real. Without an Amazon SES account with a verified sending domain, or a Cloudflare account on the Workers Paid plan with a domain onboarded for Email Sending, there is nothing for the queue worker to send through. The self-hosted part covers the dashboard, API keys, logs and webhooks, not the sending infrastructure.

Operationally, Larasend expects a queue worker and a scheduler to be running. On Laravel Cloud the README spells out both, plus PostgreSQL, Redis and durable object storage. Teams that cannot provision and monitor those processes will get an install that accepts API calls and never delivers them, which is a worse failure than a hard error.

Finally, the project is young. The only release listed is v0.1.0 from 2026-07-05, and the last push to main was on 2026-08-15. That is recent enough that the code is moving, but it also means the API surface and configuration keys are not yet settled, and the README does not document rollback or a downgrade path between versions. Treat upgrades as something to test on a copy of the database first.

Larasend versus sending straight from Laravel Mail to SES

The honest alternative is not another self-hosted product. It is the setup most Laravel teams already have: configure the SES mailer in config/mail.php, install the AWS SDK, and let each application talk to SES directly. That approach has fewer moving parts. There is no extra database, no queue worker dedicated to mail, no dashboard to keep patched, and no second API key to rotate.

The difference shows up in what you give up. With direct SES sending, delivery events go to an SNS topic or a configuration set destination that you build and maintain yourself, and each application needs its own path to that data. Suppressions live in the SES account, but nothing in your stack tracks them per project. API keys are IAM credentials, which are broader than a project-scoped key with scopes, expiration and last-used metadata.

Larasend concentrates those concerns in one place: a single activity log with status filters and search, an inspector that shows previews and headers, resend, per-source provider choice, and project-scoped API keys. The cost is the stack you now operate. If your team has one Laravel app and one sending domain, direct SES is simpler and Larasend adds a service to babysit. If you have several apps, several projects, or a support team that needs to answer "did this email arrive", the consolidation pays for itself.

Licence, upgrades and what maintenance actually costs

Larasend is MIT licensed. That permits commercial use, modification and redistribution provided the copyright notice and permission notice are retained, and it comes with no warranty. The repository includes a SECURITY.md, so security reports have a stated route. Nothing here is legal advice; if you redistribute Larasend as part of a product, have someone read the licence text rather than this paragraph.

The upgrade cost is mostly operational. The Dockerfile builds a PHP 8.4 FPM Alpine image with intl, opcache, pcntl, pdo_pgsql and the Redis extension, and copies a prebuilt frontend from a separate build stage, so a version bump means pulling a new image rather than rebuilding assets on the server. The app container runs migrations automatically on boot, while the queue and scheduler services set LARASEND_RUN_MIGRATIONS to false, which keeps migrations from running three times concurrently. On Laravel Cloud the README instructs running php artisan migrate --force as part of each deployment instead.

After a first deployment and after operational changes, the README says to run php artisan larasend:doctor from an environment with access to the production services. That command is the closest thing to a supported verification step, and it is worth putting in your deployment checklist rather than discovering a broken MIME disk when the first bounce arrives.

Editorial conclusion

Adopt Larasend if you already run Laravel, want SES or Cloudflare as the sender, and need email logs, suppressions and API keys on infrastructure you control. Skip it if you want a hosted API with zero operations, or if you cannot run a queue worker and a scheduler. Before committing, verify that a durable disk is configured for LARASEND_MIME_DISK, that LARASEND_CONTROL_MAILER points at a mailer independent of Larasend, and that php artisan larasend:doctor passes against your production services.

Frequently asked questions

What is Larasend used for?

It is a self-hosted transactional email platform for Laravel teams: it accepts sends over an HTTP API or a Laravel mail transport, queues them, and delivers them through your own Amazon SES account or Cloudflare Email Service while keeping the dashboard, API keys, delivery logs and webhooks on your infrastructure.

How do I install Larasend?

The README's quick start downloads docker-compose.yml and .env.example into an empty directory, sets APP_ENV, APP_URL, DB_PASSWORD and the database-backed session, cache and queue drivers, generates an application key with docker compose run --rm --entrypoint php app artisan key:generate --show, and then starts the stack with docker compose up -d.

Does Larasend send email through my own Amazon SES account?

Yes. The queue worker sends the raw email through the source's provider, using the Amazon SES HTTPS API or authenticated SMTP against Cloudflare Email Service, and SES publishes delivery, bounce, complaint, open and click events back to Larasend through the SES webhook.

Why does Larasend reject certain control mailers?

Authentication email is kept separate from the transactional provider so the instance is never its own only recovery path. The README states that log, array and larasend are rejected as LARASEND_CONTROL_MAILER values, and that verification, password recovery and invitations stay inactive until an administrator sets a tested, independently configured mailer.

What happens to Larasend's Cloudflare sources without event webhooks?

Cloudflare has no event webhooks and no open or click tracking, so delivery state is recorded at send time and the suppression list syncs hourly from the Cloudflare account-level list. SES is the source that returns bounce and complaint events in near real time.

Official sources

  1. License: MIT
  2. Project website
  3. README
  4. Releases
  5. savvyagents/larasend on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/savvyagents-larasend.svg)](https://hysenlabs.com/projects/savvyagents-larasend)