Skip to content

Troubleshooting

This guide covers the most common issues you will encounter running a Vectis Mail server, with concrete diagnostic steps and solutions. Start with checking service health, then find your specific issue below.

Before diving into specific issues, check the overall state of your server:

Terminal window
# Quick status of all services
vectis status
# Detailed health checks
vectis health
# Docker-level container status
docker compose ps

A healthy server shows all services as healthy with reasonable uptime. If a service is unhealthy or restarting, start with its logs:

Terminal window
# Vectis CLI (recommended)
vectis logs postfix --tail 100
vectis logs dovecot --tail 100
# Docker logs (alternative)
docker logs vectis-postfix --tail 100
docker logs vectis-dovecot --tail 100

Mail not delivering to external recipients

Section titled “Mail not delivering to external recipients”

Symptom: Messages sent from your server are not arriving at Gmail, Outlook, Yahoo, or other providers.

Terminal window
# Show the mail queue
docker exec vectis-postfix mailq
# Or via the Vectis CLI
vectis logs postfix --tail 200

If the queue contains messages, look at the reason for each deferred message. Common reasons:

Queue message Cause Solution
Connection timed out Port 25 outbound is blocked Contact your VPS provider to unblock outbound SMTP
421 Try again later Recipient server rate limiting Normal during IP warmup; wait and retry
550 5.7.1 Rejected SPF/DKIM failure or IP blocklisted Check authentication records and RBL status
Host or domain name not found DNS resolution failure Check /etc/resolv.conf inside the container

Many VPS providers block outbound port 25 by default to prevent spam. Test from your server:

Terminal window
# Test outbound connectivity
telnet gmail-smtp-in.l.google.com 25
# If telnet is not available
nc -zv gmail-smtp-in.l.google.com 25

If the connection times out, contact your VPS provider to unblock port 25. This is the single most common issue with new mail servers.

Terminal window
vectis domain check example.com

If SPF, DKIM, or DMARC are failing, see the DKIM, SPF & DMARC guide.

Terminal window
# Check RBL status via API
curl https://mail.example.com/api/v1/deliverability/rbl \
-H "Authorization: Bearer YOUR_TOKEN"

If your IP is listed on any RBL, follow the delisting process for that specific list. See deliverability best practices for details.

Symptom: External senders report that their messages bounce when sending to your domain.

Terminal window
dig MX example.com +short

The MX record must point to your mail hostname (e.g., mail.example.com), and the hostname must resolve to your server’s IP.

Terminal window
# From another machine, test connectivity
nc -zv mail.example.com 25
# Or use telnet
telnet mail.example.com 25

If the connection is refused, check:

  • Firewall rules on your server (ufw status or iptables -L)
  • VPS provider firewall/security group settings
  • That the Postfix container is running (docker compose ps)

Step 3: Check Postfix is accepting mail for the domain

Section titled “Step 3: Check Postfix is accepting mail for the domain”
Terminal window
vectis logs postfix --tail 200

Look for NOQUEUE: reject lines. Common causes:

Log message Cause Solution
Recipient address rejected: User unknown Mailbox does not exist Create the mailbox or check the address
Relay access denied Domain not in virtual_mailbox_domains Verify the domain is added and active in Vectis
Client host rejected IP blocked by Postfix restrictions Check smtpd_recipient_restrictions

If you use Cloudflare, make sure the mail hostname A record is DNS-only (grey cloud, not orange). See the Cloudflare guide.

Terminal window
# Verify the A record returns your IP, not Cloudflare's
dig A mail.example.com +short

Symptom: Your email arrives but lands in the recipient’s spam folder.

Send a test email to a Gmail address. Open it (check the spam folder), click the three dots, and select “Show original”. Look for:

Authentication-Results:
spf=pass
dkim=pass
dmarc=pass

If any of these show fail, fix the corresponding DNS record.

Send a test to mail-tester.com. A score below 7/10 indicates problems. The report will tell you exactly what is wrong.

  • No PTR record or PTR mismatch
  • IP on an RBL
  • SPF using ~all instead of -all
  • DMARC policy set to p=none
  • HTML-only email with no plain text alternative
  • Spammy content (excessive capitalisation, multiple exclamation marks)
  • New IP without warmup (see IP warmup guide)

Symptom: Postfix or Dovecot fail to start. Logs show cannot load certificate or SSL certificate not found.

Terminal window
# Check the cert-extractor and Traefik (the cert pipeline)
docker logs vectis-cert-extractor --tail 50
docker logs vectis-traefik --tail 50 | grep -iE 'acme|error|ratelimit'
# Verify certificate files exist
docker exec vectis-postfix ls -la /etc/ssl/mail/

Common causes:

  • Traefik has not completed initial certificate issuance (HTTP-01)
  • Port 80 is not reachable (required for the HTTP-01 challenge)
  • The hostname’s A record is wrong, or Let’s Encrypt rate-limited the account

Solution: Confirm port 80 is open and your hostname resolves to this server, then check the Traefik logs for the specific ACME error. The extractor writes a short-lived self-signed placeholder so mail services still start while issuance is pending. (On a custom certificate, supply valid PEM files and run vectis config apply.)

Symptom: Email clients show certificate warnings. TLS connections fail with certificate has expired.

Terminal window
# Check certificate expiry
docker exec vectis-postfix openssl x509 -in /etc/ssl/mail/fullchain.pem -noout -dates
# Traefik renews automatically; inspect its ACME activity if a renewal is overdue
docker logs vectis-traefik --tail 100 | grep -iE 'acme|renew|error'
docker logs vectis-cert-extractor --tail 50

Symptom: Email clients show “certificate does not match hostname” warnings.

Terminal window
# Check the certificate's CN and SAN
docker exec vectis-postfix openssl x509 -in /etc/ssl/mail/fullchain.pem -noout -text | grep -A1 "Subject:"

The certificate’s Common Name (CN) or Subject Alternative Name (SAN) must match the hostname your clients connect to. If you changed your mail hostname after installation, you need to reissue the certificate.

Symptom: Email client shows “authentication failed” when trying to log in.

Terminal window
# Check Dovecot auth logs
vectis logs dovecot --tail 100

Common causes:

Log message Cause Solution
auth failed with no password hash Mailbox does not exist Verify the mailbox exists: vectis mailbox list
password mismatch Wrong password Reset the password via dashboard or API
plaintext auth disabled Client connecting without TLS Configure the client to use SSL/TLS (port 993 for IMAP, 587 for SMTP)

Symptom: Cannot log into the Vectis admin dashboard.

Terminal window
# Check API logs for auth errors
vectis logs api --tail 100

If you have forgotten the admin password, you can reset it. SSH into the host and run a one-shot recovery container — the host vectis binary cannot reach Postgres (the data network is internal: true), so the reset must run inside a container on that network:

Terminal window
docker run --rm \
--network vectis_vectis-data \
-v /etc/vectis:/etc/vectis:ro \
--entrypoint vectis \
ghcr.io/veltara-works/vectis-api:latest \
admin reset-password [email protected]

The output prints the new password once. Copy it before closing the terminal.

Avoid docker compose run here. A Compose v5.x bug silently disconnects the live vectis-api container from its internal networks during the transient-container handoff, leaving the API unable to reach Postgres until manually reconnected. The docker run --network form above sidesteps that bug entirely.

If TOTP (two-factor authentication) is enabled and your authenticator app is out of sync:

  • Verify your device clock is accurate (TOTP is time-based)
  • Use backup codes if available
  • As a last resort, disable TOTP via the API with a valid TOTP code

After changing DNS records, allow time for propagation:

  • Cloudflare: typically 1-5 minutes
  • Other providers: up to 24-48 hours

Check current DNS state with:

Terminal window
# Query a specific DNS server (bypasses local cache)
dig @8.8.8.8 MX example.com +short
dig @1.1.1.1 TXT example.com +short

If your server cannot resolve its own hostname, check the DNS resolver configuration inside the container:

Terminal window
docker exec vectis-postfix cat /etc/resolv.conf

Docker containers typically use the host’s DNS configuration. If your host is configured with an internal DNS server that does not have your public records, outbound mail may fail to resolve recipient domains.

Symptom: Messages are stuck in the queue and not being delivered.

Terminal window
# View the queue
docker exec vectis-postfix mailq
# Force queue flush (attempt immediate delivery)
docker exec vectis-postfix postqueue -f
# View a specific queued message
docker exec vectis-postfix postcat -q MESSAGE_ID

If the queue contains messages that will never be delivered (e.g., invalid recipients from a misconfiguration):

Terminal window
# Delete all deferred messages
docker exec vectis-postfix postsuper -d ALL deferred
# Delete a specific message
docker exec vectis-postfix postsuper -d MESSAGE_ID

Use this carefully – deleting queued messages means those emails will never be delivered.

Symptom: IMAP clients cannot connect. Connection is refused on port 993.

Terminal window
# Check if Dovecot is running
docker compose ps | grep dovecot
# Check Dovecot logs
vectis logs dovecot --tail 100
# Test the port locally
docker exec vectis-dovecot ss -tlnp | grep 993

Common causes:

  • Dovecot container has crashed (check logs for the reason)
  • Port 993 is blocked by a firewall
  • TLS certificate is missing or invalid (Dovecot requires a valid cert to start with ssl = required)

Symptom: Server running slowly or services being OOM-killed.

Terminal window
# Check per-container memory usage
docker stats --no-stream
# Check system memory
free -h

Typical memory usage:

Service Normal range Notes
Postfix 30-80 MB Increases with queue size
Dovecot 50-200 MB Scales with concurrent IMAP connections
Rspamd 200-500 MB Neural network and Bayes classifier
ClamAV 800 MB - 1.5 GB Virus signature database
Postgres 100-300 MB Depends on shared_buffers config
Valkey 30-100 MB Depends on cached data
Traefik 30-60 MB Minimal
Go API 20-60 MB Minimal

If ClamAV is consuming too much memory, consider switching to a lighter profile or disabling it:

# In config.yaml
clamav:
profile: none # Disables ClamAV entirely

Then apply:

Terminal window
vectis config apply

Symptom: A container is restarting in a loop.

Terminal window
# Check container state
docker compose ps
# Check the exit code and restart count
docker inspect vectis-postfix --format='{{.State.ExitCode}} {{.RestartCount}}'
# Check logs for the crash reason
docker logs vectis-postfix --tail 200

Common crash causes:

Container Typical crash reason Solution
Postfix Invalid main.cf configuration Run vectis config validate and fix errors
Dovecot Missing TLS certificate Ensure Traefik has issued the cert and the cert-extractor has mirrored it
Rspamd Corrupted neural network data Delete Rspamd data volume and restart
ClamAV Out of memory during signature update Increase memory limits or disable ClamAV
Postgres Corrupted data files Restore from backup: vectis backup restore
Terminal window
# Restart a specific container
docker compose restart postfix
# Or stop and start
docker compose stop dovecot
docker compose start dovecot
Terminal window
# Restart everything
docker compose restart
# Nuclear option: stop all, remove containers, start fresh
docker compose down
docker compose up -d

If you need to file a support request or investigate a complex issue, collect this information:

Terminal window
# System info
vectis status --json > /tmp/vectis-status.json
# All service logs (last 500 lines each)
vectis logs postfix --tail 500 > /tmp/postfix.log 2>&1
vectis logs dovecot --tail 500 > /tmp/dovecot.log 2>&1
vectis logs rspamd --tail 500 > /tmp/rspamd.log 2>&1
vectis logs api --tail 500 > /tmp/api.log 2>&1
# DNS check
vectis domain check example.com --json > /tmp/dns-check.json 2>&1
# Docker stats snapshot
docker stats --no-stream > /tmp/docker-stats.txt