Installation
ThorMail is self-hosted software that you deploy on your own infrastructure. This guide covers installation using Docker (recommended) or manual setup.
Prerequisites
Section titled “Prerequisites”Before proceeding, please ensure you meet the following requirements. ThorMail is a powerful tool that requires some system administration knowledge to run effectively.
System Requirements
Section titled “System Requirements”- Docker Recent version
- Docker Compose Recent version
- PostgreSQL 18 (with
pg_partmanextension recommended) - We provide an optimized image:thormail/postgres-thormail - Redis 6+ (for caching and rate limiting) or compatible in-memory database
- Valid Email (for admin account setup)
Required Knowledge
Section titled “Required Knowledge”To ensure a smooth deployment, you should be familiar with:
- Docker & Docker Compose: Managing containers, volumes, and understanding logs.
- Networking: Configuring reverse proxies, DNS, and firewalls.
- Linux Environment: Basic command line usage and environment variables.
Fast Install (Script)
Section titled “Fast Install (Script)”For a super fast setup, you can use our installation script. This script handles the download of the Docker Compose file and basic configuration.
The installation script will automatically download the necessary Docker Compose files and generate random, secure environment variables for your instance. Simply follow the on-screen prompts to complete the setup.
# Download the install scriptcurl -fsSL https://thormail.io/install.sh -o install.sh# Run the installerbash install.shDocker Compose Installation (Recommended)
Section titled “Docker Compose Installation (Recommended)”The fastest way to get ThorMail running is with Docker Compose.
-
Create
docker-compose.ymlCreate a file named
docker-compose.ymlwith the following content:Use this for a complete, self-contained installation where all components (Database, Backend, Frontend, Worker) run on the same server.
docker-compose.ymlversion: '3.8' services: postgres: image: thormail/postgres-thormail:latest pull_policy: always environment: POSTGRES_USER: ${DB_USER} POSTGRES_PASSWORD: ${DB_PASSWORD} POSTGRES_DB: ${DB_NAME} volumes: - thormail_postgres_data:/var/lib/postgresql/data networks: - thormail_network restart: unless-stopped redis: image: docker.dragonflydb.io/dragonflydb/dragonfly networks: - thormail_network restart: unless-stopped backend: image: thormail/thormail-backend:latest pull_policy: always environment: PORT: 4000 DB_HOST: ${DB_HOST} DB_PORT: ${DB_PORT} DB_USER: ${DB_USER} DB_PASSWORD: ${DB_PASSWORD} DB_NAME: ${DB_NAME} REDIS_HOST: ${REDIS_HOST} REDIS_PORT: ${REDIS_PORT} JWT_SECRET: ${JWT_SECRET} ENCRYPTION_KEY: ${ENCRYPTION_KEY} EMAIL: ${EMAIL} ports: - "4000:4000" volumes: - thormail_backend_extensions:/app/extensions depends_on: - postgres - redis networks: - thormail_network frontend: image: thormail/thormail-frontend:latest pull_policy: always environment: NUXT_PUBLIC_API_BASE: ${NUXT_PUBLIC_API_BASE} ports: - "3000:3000" depends_on: - backend networks: - thormail_network worker: image: thormail/thormail-worker:latest pull_policy: always environment: DB_HOST: ${DB_HOST} DB_PORT: ${DB_PORT} DB_USER: ${DB_USER} DB_PASSWORD: ${DB_PASSWORD} DB_NAME: ${DB_NAME} JWT_SECRET: ${JWT_SECRET} ENCRYPTION_KEY: ${ENCRYPTION_KEY} WORKER_ID: ${WORKER_ID} WORKER_NAME: ${WORKER_NAME} volumes: - thormail_worker_extensions:/app/extensions depends_on: - postgres networks: - thormail_network networks: thormail_network: driver: bridge volumes: thormail_postgres_data: thormail_backend_extensions: thormail_worker_extensions:Use this if you are connecting to a managed database (e.g., AWS RDS, Cloud SQL) or an existing Postgres instance.
docker-compose.ymlversion: '3.8' services: backend: image: thormail/thormail-backend:latest pull_policy: always environment: PORT: 4000 DB_HOST: ${DB_HOST} DB_PORT: ${DB_PORT} DB_USER: ${DB_USER} DB_PASSWORD: ${DB_PASSWORD} DB_NAME: ${DB_NAME} JWT_SECRET: ${JWT_SECRET} ENCRYPTION_KEY: ${ENCRYPTION_KEY} EMAIL: ${EMAIL} ports: - "4000:4000" volumes: - thormail_backend_extensions:/app/extensions networks: - thormail_network frontend: image: thormail/thormail-frontend:latest pull_policy: always environment: NUXT_PUBLIC_API_BASE: ${NUXT_PUBLIC_API_BASE} ports: - "3000:3000" depends_on: - backend networks: - thormail_network worker: image: thormail/thormail-worker:latest pull_policy: always environment: DB_HOST: ${DB_HOST} DB_PORT: ${DB_PORT} DB_USER: ${DB_USER} DB_PASSWORD: ${DB_PASSWORD} DB_NAME: ${DB_NAME} JWT_SECRET: ${JWT_SECRET} ENCRYPTION_KEY: ${ENCRYPTION_KEY} WORKER_ID: ${WORKER_ID} WORKER_NAME: ${WORKER_NAME} volumes: - thormail_worker_extensions:/app/extensions networks: - thormail_network networks: thormail_network: driver: bridge volumes: thormail_backend_extensions: thormail_worker_extensions:Use this for scaling web/API nodes without running workers or databases on this host.
docker-compose.ymlversion: '3.8' services: backend: image: thormail/thormail-backend:latest pull_policy: always environment: PORT: 4000 DB_HOST: ${DB_HOST} DB_PORT: ${DB_PORT} DB_USER: ${DB_USER} DB_PASSWORD: ${DB_PASSWORD} DB_NAME: ${DB_NAME} JWT_SECRET: ${JWT_SECRET} ENCRYPTION_KEY: ${ENCRYPTION_KEY} EMAIL: ${EMAIL} ports: - "4000:4000" volumes: - thormail_backend_extensions:/app/extensions networks: - thormail_network frontend: image: thormail/thormail-frontend:latest pull_policy: always environment: NUXT_PUBLIC_API_BASE: ${NUXT_PUBLIC_API_BASE} ports: - "3000:3000" depends_on: - backend networks: - thormail_network networks: thormail_network: driver: bridge volumes: thormail_backend_extensions: -
Configure environment variables
Create a
.envfile in the same directory:.env################################################################################ # DATABASE CONFIGURATION ################################################################################ # Database hostname (service name in Docker Compose) DB_HOST=postgres # PostgreSQL port DB_PORT=5432 # Database username DB_USER=thormail # [GENERATED SECURELY] Database password DB_PASSWORD=generating... # Database name DB_NAME=thormail ################################################################################ # REDIS CONFIGURATION ################################################################################ # Redis hostname (service name in Docker Compose) REDIS_HOST=redis # Redis port REDIS_PORT=6379 ################################################################################ # SECURITY & ENCRYPTION ################################################################################ # [GENERATED SECURELY] Secret key for signing JSON Web Tokens JWT_SECRET=generating... # [GENERATED SECURELY] 32-char key for encrypting sensitive data ENCRYPTION_KEY=generating... ################################################################################ # GENERAL CONFIGURATION ################################################################################ # Owner Email - Must match your ThorMail license email EMAIL=[email protected] # Frontend API URL - Where the frontend reaches the backend NUXT_PUBLIC_API_BASE=http://localhost:4000 ################################################################################ # WORKER CONFIGURATION ################################################################################ # [GENERATED SECURELY] Unique ID for this worker node WORKER_ID=generating... # Human readable name for this worker WORKER_NAME=worker-1Security Note: The values above are securely generated in your browser each time you refresh using
crypto.getRandomValues(). They are not stored or transmitted anywhere. You can safely copy and use them for your production `.env` file.Required Changes: You must update the following variables to match your environment:
EMAIL: Needs to match the owner email address associated with your ThorMail license.NUXT_PUBLIC_API_BASE: The URL where the frontend (browser) can reach the backend.- Localhost:
http://localhost:4000 - Server IP:
http://<YOUR_SERVER_IP>:4000(if accessing remotely without a domain) - Domain:
https://api.yourdomain.com(if using a reverse proxy/domain)
- Localhost:
-
Start the services
Terminal window docker compose up -d -
Access ThorMail
Open
http://localhost:3000in your browser.
Component Installation (Docker)
Section titled “Component Installation (Docker)”For production deployments where you want to orchestrate containers individually.
We recommend using our optimized Docker image which comes pre-configured with PostgreSQL 18 and pg_partman.
Create a persistent volume for the database data (required for data persistence):
docker volume create thormail_postgres_dataThen run the container:
docker run -d \
--name thormail-postgres \
--restart unless-stopped \
-e POSTGRES_USER=thormail \
-e POSTGRES_PASSWORD=secure_password \
-e POSTGRES_DB=thormail \
-v thormail_postgres_data:/var/lib/postgresql/data \
-p 5432:5432 \
thormail/postgres-thormail:latest Create a .env file with the following environment variables:
DB_HOST=postgresDB_PORT=5432DB_USER=thormailDB_PASSWORD=secure_passwordDB_NAME=thormailJWT_SECRET=your-jwt-secretENCRYPTION_KEY=your-encryption-keyCreate a persistent volume for extensions (required for module persistence and integrity):
docker volume create thormail_backend_extensionsThen run the container:
docker run -d --name thormail-backend --restart unless-stopped -p 4000:4000 -v thormail_backend_extensions:/app/extensions --env-file .env thormail/thormail-backend:beta Create a .env file with the following environment variables:
NUXT_PUBLIC_API_BASE=http://localhost:4000Then run the container:
docker run -d --name thormail-frontend --restart unless-stopped -p 3000:3000 --env-file .env thormail/thormail-frontend:beta Create a .env file with the following environment variables:
DB_HOST=postgresDB_PORT=5432DB_USER=thormailDB_PASSWORD=secure_passwordDB_NAME=thormailJWT_SECRET=your-jwt-secretENCRYPTION_KEY=your-encryption-keyWORKER_ID=my-worker-idWORKER_NAME=worker-1Create a persistent volume for extensions (required for module persistence and integrity):
docker volume create thormail_worker_extensionsThen run the container:
docker run -d --name thormail-worker --restart unless-stopped -v thormail_worker_extensions:/app/extensions --env-file .env thormail/thormail-worker:beta Architecture Overview
Section titled “Architecture Overview”ThorMail consists of four main components:
| Component | Purpose | Port |
|---|---|---|
| Backend API | REST API, authentication, queue management | 4000 |
| Frontend | Web dashboard for configuration and monitoring | 3000 |
| Worker | Message processing, adapter execution | - |
| Database | PostgreSQL 18 with pg_partman for queue and data | 5432 |
┌─────────────┐ ┌─────────────┐ ┌─────────────┐│ Your App │────▶│ Backend │────▶│ PostgreSQL │└─────────────┘ │ API │ │ (Queue) │ └─────────────┘ └──────┬──────┘ │ ┌─────────────┐ │ │ Worker(s) │◀────────────┘ └──────┬──────┘ │ ┌────────────┼────────────┐ ▼ ▼ ▼ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ SendGrid│ │ SMTP │ │ Twilio │ └─────────┘ └─────────┘ └─────────┘First-Time Setup & Admin Password
Section titled “First-Time Setup & Admin Password”When ThorMail starts for the first time, it automatically creates the initial administrator account using the EMAIL address defined in your configuration. For security, a random password is generated and displayed in the backend logs.
Finding the Password in Logs
Section titled “Finding the Password in Logs”Since ThorMail runs several processes via PM2, search for the thormail-master logs within the backend container.
To see the initial setup message and generated password, run:
docker logs thormail-backendYou will see a message similar to this:
🔐 FIRST USER SETUP REQUIRED
No users found in the database.Initial admin user created with: [email protected]A random password has been generated for your security.
Admin Password: [SOME_RANDOM_PASSWORD]Quick Command
Section titled “Quick Command”To quickly extract only the password from your logs, you can run:
docker logs thormail-backend 2>&1 | grep "Admin Password:"Verifying Installation
Section titled “Verifying Installation”After installation, verify everything is working:
-
Check Backend Health
Terminal window curl http://localhost:4000/health -
Check Worker Status
- The worker should appear in the dashboard under “Workers”
-
Activate License
- Go to Settings → License
- Enter your license key
Next Steps
Section titled “Next Steps”Your ThorMail installation is ready! Continue to the Quick Start Guide to send your first message.