Deploy On A Subdomain
Use one subdomain for the admin dashboard and one for the SMTP service. Keep the dashboard behind HTTPS and keep real sending disabled until DNS and reputation checks are ready.
Create DNS Records
Add these at your domain registrar or DNS provider.
panel.yourdomain.com A YOUR_SERVER_IP
smtp.yourdomain.com A YOUR_SERVER_IP
# Reverse DNS/PTR is set at your VPS/server provider:
YOUR_SERVER_IP -> smtp.yourdomain.com
Upload The App
From your local project folder, copy the app to the server.
scp -r . root@YOUR_SERVER_IP:/opt/independent-smtp-provider
Configure Environment
On the server, set production values. Start in file mode first.
cd /opt/independent-smtp-provider
cp .env.example .env
nano .env
PUBLIC_HOSTNAME=smtp.yourdomain.com
API_HOST=127.0.0.1
API_PORT=8080
SMTP_HOST=0.0.0.0
SMTP_PORT=2525
SMTP_TRANSPORT=file
ADMIN_TOKEN=use-a-long-random-secret
Run With PM2
PM2 keeps the Node app alive after you close SSH.
npm install -g pm2
npm install
node cli.js init
pm2 start src/index.js --name smtp-provider
pm2 save
pm2 startup
Nginx Reverse Proxy
Expose the dashboard on HTTPS without exposing port 8080 directly.
server {
server_name panel.yourdomain.com;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Add HTTPS
After Nginx is configured and DNS points to the server, issue TLS.
sudo nginx -t
sudo systemctl reload nginx
sudo certbot --nginx -d panel.yourdomain.com
Open Firewall Ports
Use 80/443 for the panel and 2525 or 587 for SMTP submission.
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 2525/tcp
# Later, if using standard submission:
sudo ufw allow 587/tcp
Turn On Real Sending Later
Only switch to direct mode after SPF, DKIM, DMARC, PTR/rDNS, TLS, abuse mailbox, and warmup are ready.
SMTP_TRANSPORT=direct
pm2 restart smtp-provider --update-env
Customer DNS
Generated per customer domainWhen you run node cli.js create-domain <accountId> customer.com, the app prints the TXT records the customer must publish for ownership and DKIM. Do not allow sending from a customer domain until those records verify.