---
title: "Deploy SendPortal"
description: "SendPortal template with PostgreSQL & Redis"
category: "CMS"
url: https://railway.com/deploy/sendportal
---

# Deploy SendPortal

SendPortal template with PostgreSQL & Redis

**[Deploy SendPortal on Railway](https://railway.com/template/sendportal)**

- **Creator:** Xav's Projects
- **Category:** CMS
- **Total deploys:** 10

## Template content

### Postgres https://devicons.railway.app/i/postgresql.svg

- **Image:** ghcr.io/railwayapp-templates/postgres-ssl:17

### Redis https://cdn.sanity.io/images/sy1jschh/production/0ce0bfdcfbdbf69662b1116671f97c2dd788b655-157x157.svg

- **Image:** redis:8.2.1
- **Start command:** `/bin/sh -c "rm -rf $RAILWAY_VOLUME_MOUNT_PATH/lost+found/ && exec docker-entrypoint.sh redis-server --requirepass $REDIS_PASSWORD --save 60 1 --dir $RAILWAY_VOLUME_MOUNT_PATH"`

### sendportal https://raw.githubusercontent.com/XavTo/sendportal-Selfhost/refs/heads/master/sendportal.png

- **Source:** XavTo/sendportal-Selfhost
- **Health check:** /api/health
- **Public domain:** Yes

## Documentation

# Deploy and Host SendPortal with Railway

**SendPortal** is an open‑source, self‑hosted email marketing app (built on Laravel) for creating lists, tags, templates and sending campaigns via your own email provider. It is lightweight, database‑backed, and supports multiple ESPs (Postmark, SES, Mailgun, SendGrid, Mailjet, SMTP).

## About Hosting SendPortal

This template provisions a ready‑to‑run container that serves SendPortal (via FrankenPHP/Caddy), a PostgreSQL database for application data, and Redis for queues/caching. Laravel Horizon is enabled to process SendPortal queues (`sendportal-message-dispatch`, `sendportal-webhook-process`) and the Laravel scheduler runs every minute to progress campaigns and housekeeping tasks. A health endpoint is exposed at `/health` for Railway checks.

## Common Use Cases

- Self‑host newsletters with your own ESP billing (no per‑subscriber fees).
- Run marketing campaigns and drip sequences using your existing infrastructure.
- Keep data in your own database with team‑member invites and roles.
- Control tracking (opens/clicks) via provider webhooks handled by SendPortal.

## Dependencies for SendPortal Hosting

- **PostgreSQL**: primary application database with persistent storage.
- **Redis**: queues and cache; required by Horizon.
- **Queue workers**: Horizon or `queue:work` consuming `sendportal-message-dispatch` and `sendportal-webhook-process`.
- **Scheduler**: `php artisan schedule:run` executed every minute inside the container.
- **Email Service Provider (ESP)**: Postmark, SES, Mailgun, SendGrid, Mailjet, or SMTP account.
- **Verified sender domain** at the ESP for deliverability and link tracking.
- **Application configuration**: `APP_URL`, `APP_KEY`, `APP_ENV`, `QUEUE_CONNECTION=redis`, etc.
- **Health check**: `/health` endpoint returning `OK` for Railway.

> **Important:** App‑level mail settings (`MAIL_*`) are used only for **user management emails** (signup, invite, password reset). For campaign emails you must create an **Email Service** inside the SendPortal UI (left menu → **Email Services**) and select your ESP (SES, Postmark, Mailgun, SendGrid, Mailjet, SMTP).

### Deployment Dependencies

- **PostgreSQL**
- **Redis**

#### One‑click deploy (what happens)

1. The container boots, publishes vendor assets, runs DB migrations.
2. Horizon is started to process queued jobs via Redis.
3. A cron inside the container triggers `php artisan schedule:run` every minute.
4. The app exposes `/health` returning `OK` for Railway health checks.

#### Environment variables (required / recommended)

> The values below are **examples**. Replace them with your own credentials.

##### Core app

| Variable | Required | Example | Notes |
|---|---|---|---|
| `APP_NAME` | ✅ | `SendPortal` | Display name used in emails/UI. |
| `APP_ENV` | ✅ | `production` | `production` recommended. |
| `APP_KEY` | ✅ | (random 32‑char) | Laravel encryption key. Generate once and keep stable. |
| `APP_URL` | ✅ | `https://your-app.up.railway.app` | Used to build links (unsubscribe, verification, webhooks). |
| `APP_DEBUG` | ⛔ | `false` | Keep `false` in production. |
| `SESSION_DRIVER` | ✅ | `redis` | Matches the bundled Redis. |
| `CACHE_DRIVER` | ✅ | `redis` | — |
| `QUEUE_CONNECTION` | ✅ | `redis` | Required for Horizon/queues. |
| `SESSION_LIFETIME` | ➖ | `120` | Minutes. |
| `APP_TIMEZONE` | ➖ | `Europe/Paris` | Requires `config/app.php: 'timezone' => env('APP_TIMEZONE','UTC')`. |

##### Database (PostgreSQL)

| Variable | Required | Example |
|---|---|---|
| `DB_CONNECTION` | ✅ | `pgsql` |
| `DB_HOST` | ✅ | Provided by Railway Postgres plugin |
| `DB_PORT` | ✅ | `5432` |
| `DB_DATABASE` | ✅ | Provided by Railway Postgres plugin |
| `DB_USERNAME` | ✅ | Provided by Railway Postgres plugin |
| `DB_PASSWORD` | ✅ | Provided by Railway Postgres plugin |

##### Redis

| Variable | Required | Example |
|---|---|---|
| `REDIS_HOST` | ✅ | Provided by Railway Redis plugin |
| `REDIS_PORT` | ✅ | Provided by Railway Redis plugin |
| `REDIS_PASSWORD` | ✅ | Provided by Railway Redis plugin |

##### Mail (used **only** for user‑management emails)

| Variable | Required | Example | Description |
|---|---|---|---|
| `MAIL_MAILER` | ✅ | `smtp` | App mailer for login/register/reset. Options: `smtp`, `sendmail`, `ses`, `mailgun`, `postmark`. |
| `MAIL_HOST` | ✅ (if smtp) | `pro2.mail.ovh.net` | SMTP server host. |
| `MAIL_PORT` | ✅ (if smtp) | `587` | Typically `587` (TLS), `465` (SSL), or `25`. |
| `MAIL_USERNAME` | ✅ (if smtp) | `info@yourdomain.com` | SMTP username / full email. |
| `MAIL_PASSWORD` | ✅ (if smtp) | `your-secure-password` | SMTP password. |
| `MAIL_ENCRYPTION` | ➖ | `tls` | `tls`, `ssl`, or `null`. |
| `MAIL_FROM_ADDRESS` | ✅ | `info@yourdomain.com` | From address for system emails. |
| `MAIL_FROM_NAME` | ✅ | `${APP_NAME}` | From name for system emails. |

**Provider‑specific (if not using SMTP):**

- **Postmark**: set `MAIL_MAILER=postmark` and `POSTMARK_TOKEN=your-token`
- **Mailgun**: set `MAIL_MAILER=mailgun` and provide `MAILGUN_DOMAIN`, `MAILGUN_SECRET`, optional `MAILGUN_ENDPOINT`
- **AWS SES**: set `MAIL_MAILER=ses` and provide `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_DEFAULT_REGION`

> SendGrid/Mailjet are available as **Email Services inside SendPortal** for campaign sending. They do **not** use the `MAIL_*` envs – configure them in **SendPortal → Email Services**.

#### Health check (Railway)

Set the health check path to:

```
/health
```

Expected status: **200 OK** with body `OK`.

#### How campaigns are dispatched

1. You create a campaign, pick recipients (list/tag), and **Send / Queue** it.
2. The job enters `sendportal-message-dispatch` (visible in **/horizon**).
3. Horizon workers pull from Redis and send through your **Email Service** (configured in the UI).
4. Webhooks (provider dependent) are handled by jobs in `sendportal-webhook-process`.

If you do not see jobs in Horizon, verify:
- `QUEUE_CONNECTION=redis` and Redis credentials are correct.
- Your **Email Service** exists and sends a **successful test email**.
- The scheduler is running (container includes cron to run `schedule:run` each minute).

## Why Deploy SendPortal on Railway?

Railway is a singular platform to deploy your infrastructure stack. Railway will host your infrastructure so you do not have to deal with configuration, while allowing you to vertically and horizontally scale it. By deploying SendPortal on Railway, you are one step closer to supporting a complete full‑stack application with minimal burden. Host your servers, databases, and more on Railway.


## Similar templates

- [Libredesk - Complete Setup](https://railway.com/deploy/libredesk-complete-setup) — Complete self-hosted omnichannel customer support desk.
- [Paperless-ngx](https://railway.com/deploy/paperless-ngx-3) — Paperless-ngx — document management with OCR and full-text search
- [Instatic CMS - Postgres](https://railway.com/deploy/instatic-cms-postgres) — Design, build and manage powerful static sites from state-of-the-art CMS

Open this page in a browser: https://railway.com/deploy/sendportal
