# How to Configure Email Notifications with SMTP Settings in TREK

> Configure TREK email notifications using SMTP settings. Learn how to set up email alerts for password resets, trip invites, and booking changes via environment variables or the Admin UI.

- Repository: [Maurice/TREK](https://github.com/mauriceboe/TREK)
- Tags: how-to-guide
- Published: 2026-07-04

---

**TREK sends email notifications for password resets, trip invites, and booking changes through an SMTP server configured either via environment variables or the Admin UI, with settings merged by the `getSmtpConfig()` function in [`server/src/services/notifications.ts`](https://github.com/mauriceboe/TREK/blob/main/server/src/services/notifications.ts).**

The TREK open-source travel management platform uses **nodemailer** to dispatch transactional emails. You can configure the SMTP connection using two methods: environment variables for infrastructure-as-code deployments, or the database-backed Admin UI for runtime configuration. Both methods ultimately feed into the same notification service that handles encryption, transport creation, and error logging.

## Configuration Sources and Priority

TREK reads SMTP settings from two locations and merges them with a clear precedence order:

1. **Environment variables** (highest priority): `SMTP_HOST`, `SMTP_PORT`, `SMTP_USER`, `SMTP_PASS`, `SMTP_FROM`, `SMTP_SKIP_TLS_VERIFY`
2. **Database settings** (`app_settings` table): `smtp_host`, `smtp_port`, `smtp_user`, `smtp_pass`, `smtp_from`, `smtp_skip_tls_verify`

The `getSmtpConfig()` function in [`server/src/services/notifications.ts`](https://github.com/mauriceboe/TREK/blob/main/server/src/services/notifications.ts) implements this fallback logic:

```typescript
function getSmtpConfig(): SmtpConfig | null {
  const host = process.env.SMTP_HOST || getAppSetting('smtp_host');
  const port = process.env.SMTP_PORT || getAppSetting('smtp_port');
  const user = process.env.SMTP_USER || getAppSetting('smtp_user');
  const pass = process.env.SMTP_PASS || decrypt_api_key(getAppSetting('smtp_pass')) || '';
  const from = process.env.SMTP_FROM || getAppSetting('smtp_from');
  if (!host || !port || !from) return null;
  return { host, port: parseInt(port, 10), user: user || '', pass: pass || '', from, secure: parseInt(port, 10) === 465 };
}

```

**Important:** The `smtp_pass` value stored in the database is encrypted using the `ENCRYPTION_KEY`. If you rotate or lose this key, the stored password becomes unreadable and must be re-entered.

## Setting SMTP Variables

### Via Docker and Environment Variables

For containerized deployments, export the SMTP variables in your [`docker-compose.yml`](https://github.com/mauriceboe/TREK/blob/main/docker-compose.yml) or `.env` file. Port 465 enables implicit TLS, while 587 uses STARTTLS:

```yaml
services:
  trek:
    image: mauriceboe/trek:latest
    environment:
      - SMTP_HOST=smtp.example.com
      - SMTP_PORT=587
      - SMTP_USER=trekmailer
      - SMTP_PASS=super-secret
      - SMTP_FROM=TREK <noreply@example.com>
      - SMTP_SKIP_TLS_VERIFY=false
      - ENCRYPTION_KEY=$(openssl rand -hex 32)

```

Place the `.env` file adjacent to your compose file, or inject the variables directly via your orchestration platform.

### Via the Admin Panel

If you prefer runtime configuration without restarting containers:

1. Log in as an administrator.
2. Navigate to **Admin → Notifications**.
3. In the **Email (SMTP)** panel, fill in **Host**, **Port**, **User**, **Password**, **From address**, and optionally **Skip TLS verification**.
4. Click **Save** to persist encrypted values to the `app_settings` table.

The UI exposes the same fields as the environment variables, offering flexibility for non-technical administrators.

## How TREK Uses SMTP Configuration

Once configured, TREK initializes a nodemailer transport on demand for two primary notification types:

- **Password resets**: The `sendPasswordResetEmail()` function generates a one-time link. If no SMTP configuration is present, the link falls back to the server console.
- **Event notifications**: The `sendEmail()` function handles trip invites, booking changes, and custom alerts. It assembles HTML via `buildEmailHtml()` and transmits messages through the transport created from `getSmtpConfig()`.

Both functions log outcomes using `logInfo` and `logError`, enabling visibility into delivery success or transport failures via container logs.

## Testing Your SMTP Configuration

TREK provides a built-in test helper accessible programmatically or through the UI. The `testSmtp()` function in [`server/src/services/notifications.ts`](https://github.com/mauriceboe/TREK/blob/main/server/src/services/notifications.ts) validates connectivity by sending a probe message:

```typescript
export async function testSmtp(to: string): Promise<{ success: boolean; error?: string }> {
  if (!getSmtpConfig()) return { success: false, error: 'SMTP not configured' };
  try {
    const config = getSmtpConfig()!;
    const skipTls = process.env.SMTP_SKIP_TLS_VERIFY === 'true' || getAppSetting('smtp_skip_tls_verify') === 'true';
    const transporter = nodemailer.createTransport({
      host: config.host,
      port: config.port,
      secure: config.secure,
      auth: config.user ? { user: config.user, pass: config.pass } : undefined,
      ...(skipTls ? { tls: { rejectUnauthorized: false } } : {}),
    });
    await transporter.sendMail({
      from: config.from,
      to,
      subject: 'TREK — Test Notification',
      text: 'This is a test email from TREK. If you received this, your SMTP configuration is working correctly.',
    });
    return { success: true };
  } catch (err) {
    return { success: false, error: err instanceof Error ? err.message : 'Unknown error' };
  }
}

```

**Manual API test**:

```bash
curl -X POST http://localhost:3001/api/admin/notifications/test-smtp \
     -H "Authorization: Bearer <admin-jwt>" \
     -d '{"email":"you@example.com"}'

```

A successful response returns `{"success": true}` and delivers the test message to your inbox.

## Troubleshooting Common SMTP Issues

| Symptom | Likely Cause | Fix |
|---------|--------------|-----|
| No email arrives; logs show "SMTP not configured" | Missing `SMTP_HOST`, `SMTP_PORT`, or `SMTP_FROM` | Verify all three required variables are set in environment or Admin UI |
| Connection refused or timeout | Incorrect host/port or firewall blocking outbound SMTP | Test connectivity with `nc -zv $SMTP_HOST $SMTP_PORT` from inside the container |
| TLS handshake error | Using port 587 with `secure: true` or self-signed certificates | Use `SMTP_SKIP_TLS_VERIFY=true` only for internal self-signed servers, or switch to port 465 for implicit TLS |
| Password reset link appears in console | No SMTP configured or environment variables not loaded | Set SMTP variables and restart the container if using env vars |
| SMTP password suddenly invalid after restart | `ENCRYPTION_KEY` changed or lost | Maintain a persistent `ENCRYPTION_KEY` in a volume or secret store; otherwise re-enter the password in the Admin UI |

## Summary

- **TREK supports dual configuration paths**: Environment variables take precedence over database settings stored in the `app_settings` table.
- **Encryption is mandatory**: The `ENCRYPTION_KEY` protects sensitive values like `SMTP_PASS`; losing it requires reconfiguration.
- **Testing is built-in**: Use the `testSmtp()` function or Admin UI button to verify connectivity before production use.
- **Logs provide visibility**: Check server logs for `logInfo` and `logError` output from `sendEmail()` and `sendPasswordResetEmail()` to diagnose delivery issues.
- **Port selection matters**: Use 465 for implicit TLS or 587 for STARTTLS, matching your provider's requirements.

## Frequently Asked Questions

### Can I use TREK without configuring SMTP?

Yes. If `SMTP_HOST`, `SMTP_PORT`, and `SMTP_FROM` are unset and the database fields are empty, TREK operates without email functionality. Password reset links will appear in the server console instead of being emailed, and trip notifications will not be sent.

### Why does my SMTP password stop working after a container restart?

The password stored in the database is encrypted with the `ENCRYPTION_KEY`. If this key changes or is not persisted across restarts (e.g., using `$(openssl rand -hex 32)` without saving the output), TREK cannot decrypt the password. Store the key in a persistent volume or secret management system, or re-enter the password via the Admin UI.

### How do I send custom emails from my TREK extension?

Import the `sendEmail` function from [`server/src/services/notifications.ts`](https://github.com/mauriceboe/TREK/blob/main/server/src/services/notifications.ts) and invoke it with recipient, subject, body, user ID, and optional path:

```typescript
import { sendEmail } from '@trek/server/services/notifications';

await sendEmail('user@example.com', 'Custom Alert', 'Message body', 42, '/dashboard');

```

This reuses the existing SMTP transport and logging infrastructure.

### What is the difference between `SMTP_SKIP_TLS_VERIFY` and choosing port 465 versus 587?

`SMTP_SKIP_TLS_VERIFY` disables certificate validation for self-signed certificates and should only be used in development environments. Port 465 enables implicit TLS (secure connection from the start), while port 587 uses STARTTLS (upgrade to TLS after connection). Use 465 with `secure: true` or 587 with `secure: false` (STARTTLS) depending on your provider's requirements.