> ## Documentation Index
> Fetch the complete documentation index at: https://docs.usesend.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Self hosting useSend

> An end-to-end guide on how to self-host useSend. An open-source sending infrastructure for developers.

## Prerequisites

* A [GitHub](https://github.com) account
* An [AWS](https://aws.amazon.com) account

If you have any questions join [#self-host](https://discord.gg/gbsvjb9MqV) on discord.

## Step 1: Environment variables

useSend depends on AWS SES to send emails and SNS to receive email status. Along with that it also depends on Postgres as a database and Redis for queue. Copy the `.env.selfhost.example` file to `.env` and fill in the values.

**1. AWS credentials**

tl;dr: Login to your AWS console and create a new user with programmatic access. Attach the `AmazonSNSFullAccess` and `AmazonSESFullAccess` policies to the user. Then create a new access key for the user.

Add the following environment variables.

```env theme={null}
AWS_ACCESS_KEY_ID=<access-key-id>
AWS_SECRET_ACCESS_KEY=<secret-access-key>
```

> [!TIP]
>
> Follow this for detailed steps: [Create AWS
> credentials](https://docs.usesend.com/get-started/create-aws-credentials)

**2. GitHub app credentials for login**

useSend uses GitHub authentication for login.

Use this link to [create an github app](https://docs.github.com/en/apps/creating-github-apps/about-creating-github-apps/about-creating-github-apps)

Callback URL : `https://<your-usesend-instance>/api/auth/callback/github`

Under **Permissions & events**, set **Account permissions → Email addresses**
to **Read-only**. useSend needs the authenticated user's email address to match
new accounts to team invitations.

![github app](https://mintcdn.com/ossapps/Rec2gO3NLCb9uLZA/images/github-callback.png?fit=max\&auto=format\&n=Rec2gO3NLCb9uLZA\&q=85\&s=82be9f3c6a9017c04fe74fd22fc25eee)

Add the following environment variables.

```env theme={null}
GITHUB_ID="<your-github-client-id>"
GITHUB_SECRET="<your-github-client-secret>"
```

> [!NOTE]
>
> If you want email/password login, please help us out with the [code](https://github.com/usesend/usesend)

**3. Database & Redis**

useSend uses Postgres as a database and Redis as a queue. You need to create a new database and add the following environment variables.

If you're using docker-compose, it's all automatically done for you.

```env theme={null}
DATABASE_URL="postgres://<username>:<password>@<host>:<port>/<database-name>"
REDIS_URL="redis://<username>:<password>@<host>:<port>"
```

If you're sharing a Redis instance across multiple apps and using Redis ACL for isolation, set `REDIS_KEY_PREFIX` to namespace all keys (e.g. `REDIS_KEY_PREFIX="usesend"` prefixes all keys with `usesend:`).

**4. Next auth url and secret**

Url is the app url you're going to use and secret is random string. You can generate a random secret using this command.

```sh theme={null}
openssl rand -base64 32
```

Add the following environment variables.

```env theme={null}
  NEXTAUTH_URL="https://<your-usesend-instance>"
  NEXTAUTH_SECRET="<your-usesend-secret>"
```

**5. Email content retention (optional)**

By default, stored email content stays in the database forever. To bound database growth, set `EMAIL_CLEANUP_DAYS` to a positive number of days. A daily job (midnight UTC) then clears the `text`, `html`, `attachments` and `headers` values for emails older than the cutoff. Delivery status and event history are kept.

```env theme={null}
EMAIL_CLEANUP_DAYS=30
```

Leave it unset to keep email content indefinitely.

**6. Image uploads (optional)**

Images added in the email editor are stored in S3-compatible storage. The docker-compose setup runs [RustFS](https://github.com/rustfs/rustfs) for this and creates the bucket for you. Set its credentials:

```env theme={null}
RUSTFS_ACCESS_KEY="<access-key>"
RUSTFS_SECRET_KEY="<secret-key>"
```

To turn uploads on, expose the RustFS API (port `9002`, bound to `127.0.0.1`) through your reverse proxy and set both URLs. The browser uploads to `S3_COMPATIBLE_API_URL`, and sent emails link to `S3_COMPATIBLE_PUBLIC_URL`, so both must be reachable from the internet. Objects in the bucket are publicly readable so images load in inboxes; nothing else is.

```env theme={null}
S3_COMPATIBLE_API_URL="https://s3.example.com"
S3_COMPATIBLE_PUBLIC_URL="https://s3.example.com/unsend"
```

Leave the URLs empty to disable uploads. If you run the image without docker-compose, you can point it at any S3-compatible service (Cloudflare R2, AWS S3) by setting `S3_COMPATIBLE_ACCESS_KEY`, `S3_COMPATIBLE_SECRET_KEY`, `S3_COMPATIBLE_API_URL`, `S3_COMPATIBLE_PUBLIC_URL` and `S3_COMPATIBLE_BUCKET`.

#### Upgrading from the MinIO setup

MinIO's community edition is no longer maintained, and its `minio/minio` image has been removed from Docker Hub, so docker-compose now runs RustFS. Your existing `MINIO_ROOT_USER` and `MINIO_ROOT_PASSWORD` keep working as RustFS credentials.

RustFS starts with a new, empty `rustfs` volume. Your old MinIO volume is not touched or deleted. The stock compose file never connected the app to MinIO, so most installs have nothing in it. If you configured uploads yourself, copy the files across once after upgrading (run from the folder with your `.env`, while the new stack is up):

```sh theme={null}
set -a; . ./.env; set +a
docker run -d --name usesend-minio-migrate --network usesend-prod_default \
  -v usesend-prod_storage:/data -e MINIO_ROOT_USER -e MINIO_ROOT_PASSWORD \
  minio/minio server /data --address :9000
sleep 5
docker run --rm --network usesend-prod_default \
  -e MINIO_ROOT_USER -e MINIO_ROOT_PASSWORD \
  -e NEW_KEY="${RUSTFS_ACCESS_KEY:-$MINIO_ROOT_USER}" -e NEW_SECRET="${RUSTFS_SECRET_KEY:-$MINIO_ROOT_PASSWORD}" \
  -e BUCKET="${S3_COMPATIBLE_BUCKET:-unsend}" -e AWS_DEFAULT_REGION=us-east-1 \
  --entrypoint sh amazon/aws-cli:2.34.30 -ec '
    AWS_ACCESS_KEY_ID=$MINIO_ROOT_USER AWS_SECRET_ACCESS_KEY=$MINIO_ROOT_PASSWORD \
      aws --endpoint-url http://usesend-minio-migrate:9000 s3 sync "s3://$BUCKET" /tmp/data
    AWS_ACCESS_KEY_ID=$NEW_KEY AWS_SECRET_ACCESS_KEY=$NEW_SECRET \
      aws --endpoint-url http://rustfs:9000 s3 sync /tmp/data "s3://$BUCKET"'
docker rm -f usesend-minio-migrate
```

This uses the `minio/minio` image already on your server. If your public storage URL stays the same, images in emails you already sent keep loading. Once you've checked them, you can remove the old volume with `docker volume rm usesend-prod_storage`.

**7. Webhooks to internal hosts (optional)**

When self-hosted, webhooks can be sent to services on your internal network, such as `http://n8n:5678` on the same Docker network. Anyone who can create a webhook can then make the server send requests to private, loopback, and link-local addresses, including `http://localhost` and cloud metadata at `169.254.169.254`.

If you don't trust every user who can create webhooks on your instance, only allow publicly reachable webhook URLs:

```env theme={null}
WEBHOOK_ALLOW_PRIVATE_URLS=false
```

With this set, webhook URLs that point at or resolve to a non-public address are rejected when saved, and deliveries to them fail. Redirects from webhook endpoints are never followed.

**8. Instance admin (optional)**

The instance admin manages instance-wide settings such as SES configurations from the Admin page. By default this is the first user who signed up. To choose a different user, set `ADMIN_EMAIL` to their email address. When it is set, only that user is the admin, and the address must match their account email exactly. Other users on the instance no longer see the Admin page. If no account has that exact email, the first user who signed up stays the admin and a warning is logged.

```env theme={null}
ADMIN_EMAIL="you@example.com"
```

## Step 2: Setting up the app

You can use any platform that supports Docker to host useSend. If you have any questions drop in the [discord channel](https://discord.gg/gbsvjb9MqV) and we'll try to help you out.

### Docker

Follow this guide to setup your docker instance: [Set up docker](https://docs.usesend.com/get-started/set-up-docker)

[![Docker image](https://img.shields.io/badge/dockerhub-images-important.svg?logo=Docker)](https://hub.docker.com/r/usesend/usesend)

## Step 3: Setting up a region

In order to send emails, you need to select a region in AWS. Use a region where your users are located / where useSend is hosted. If you're confused just use `us-east-1`.

You can check available regions [here](https://docs.aws.amazon.com/general/latest/gr/ses.html)

Once you log in to useSend, it will prompt you add SES configuration.

* Add the region
* Add the callback url, which is basically the app url. Note this should be accesible from internet. This is how you get the delivery status of the emails.
* You don't need to update the send rate, it's automatically based on your account.

![github app](https://mintcdn.com/ossapps/Rec2gO3NLCb9uLZA/images/ses-settings/add-ses-settings.png?fit=max\&auto=format\&n=Rec2gO3NLCb9uLZA\&q=85\&s=afa88e609bbe014d5862862ee8340325)

> [!NOTE]
>
> useSend checks the AWS signature on every SNS message it receives at the callback URL and rejects any that fail. If email statuses stop updating past "Sent", look for `Rejected SNS message` in the logs. Setting `SNS_SIGNATURE_VERIFICATION=log-only` accepts those messages while you investigate. You also need `log-only` (together with `AWS_SNS_ENDPOINT`) if you use an SNS emulator such as LocalStack instead of AWS, because emulators don't sign their messages.

## Step 4: Getting out of sandbox mode

> [!WARNING]
>
> Don't forget to get the SES account out of sandbox mode.

![github app](https://mintcdn.com/ossapps/Rec2gO3NLCb9uLZA/images/ses-settings/sandbox.png?fit=max\&auto=format\&n=Rec2gO3NLCb9uLZA\&q=85\&s=da535d337af95e9c3d1bdf8ac63135a5)

## Step 5: SMTP Proxy Server (Optional)

The SMTP proxy server is an optional component that allows applications to send emails through useSend using standard SMTP protocol instead of the REST API. This is useful for legacy applications, email clients, or any software that needs to send emails via SMTP.

> [!TIP]
>
> The complete source code for the SMTP proxy server is available at:
> [usesend/usesend/tree/main/apps/smtp-server](https://github.com/usesend/usesend/tree/main/apps/smtp-server)

### When to use the SMTP proxy:

* **Legacy applications** that only support SMTP
* **Email clients** like Thunderbird, Outlook, Apple Mail
* **Applications** that can't easily integrate with REST APIs
* **Third-party software** that requires SMTP configuration

### Docker Compose Setup

Create a `docker-compose.yml` file for the SMTP server:

```yaml theme={null}
name: usesend-smtp-server

services:
  smtp-server:
    container_name: usesend-smtp-server
    image: usesend/smtp-proxy:latest
    environment:
      SMTP_AUTH_USERNAME: "usesend" # Username for SMTP authentication
      USESEND_BASE_URL: "https://your-usesend-instance.com" # Your useSend instance URL

      # Optional: SSL certificate paths for secure connections
      # USESEND_API_KEY_PATH: "/certs/server.key"
      # USESEND_API_CERT_PATH: "/certs/server.crt"

    # Optional: Mount SSL certificates
    # volumes:
    #   - ./certs/server.key:/certs/server.key:ro
    #   - ./certs/server.crt:/certs/server.crt:ro

    ports:
      - "25:25" # Standard SMTP
      - "587:587" # SMTP with STARTTLS
      - "2587:2587" # Alternative SMTP port
      - "465:465" # SMTP over SSL/TLS
      - "2465:2465" # Alternative SMTPS port

    restart: unless-stopped
```

Run the SMTP server:

```bash theme={null}
docker-compose up -d
```

### SMTP Configuration

To send emails through the proxy, configure your application with these SMTP settings:

* **Host**: Your server's IP address or domain
* **Ports**: 25, 587 (STARTTLS), 465 (SSL/TLS), 2587, or 2465
* **Username**: `usesend` (or your custom `SMTP_AUTH_USERNAME`)
* **Password**: Your useSend API key
* **Encryption**: STARTTLS (ports 25, 587, 2587) or SSL/TLS (ports 465, 2465)

> [!TIP]
>
> The SMTP proxy forwards all emails to your useSend instance, so make sure your
> main useSend application is running and accessible.

> [!WARNING]
>
> Ensure your firewall allows traffic on the SMTP ports you're using. For
> production deployments, consider using non-standard ports (2587, 2465) to
> avoid conflicts.

## Next steps

You're all set up now.

* Setup a domain.
* Create an API key.
* Start sending emails.

If you have any questions, please join [#self-host](https://discord.gg/gbsvjb9MqV) on discord.

> [!TIP]
>
> A community member shared a short write-up on hosting useSend with
> [Coolify](https://mattstein.com/thoughts/coolify-unsend/). Give it a read if
> you need another reference.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.