Skip to content

Deployment

Anton Popov edited this page Nov 10, 2026 · 1 revision

Deployment

Process

npm run build
NODE_ENV=production PORT=3000 node dist-server/index.mjs

deploy/goodmail.service is an example systemd unit. Keep the process on 127.0.0.1 and put a TLS reverse proxy in front.

Reverse proxy (Apache)

The page and the API are both served by the Node process. A minimal HTTPS virtual host:

<VirtualHost *:443>
    ServerName goodmail.example.com
    ProxyPreserveHost On
    ProxyPass        /.well-known/acme-challenge/ !
    ProxyPass        / http://127.0.0.1:3000/ timeout=120
    ProxyPassReverse / http://127.0.0.1:3000/
    RequestHeader set X-Forwarded-Proto "https"
    LimitRequestBody 52428800
    Header always set Strict-Transport-Security "max-age=31536000"
    SSLEngine on
    SSLCertificateFile    /etc/letsencrypt/live/goodmail.example.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/goodmail.example.com/privkey.pem
</VirtualHost>
  • ProxyPreserveHost On is required: login by host and passkeys depend on the original Host.
  • Set GOODMAIL_TRUST_PROXY=true so rate limits see the real client address.
  • LimitRequestBody caps attachment size (here 50 MB).
  • For several domains on one install see Several Domains and deploy/apache-multidomain.conf.
  • ServerTokens Prod and ServerSignature Off keep the Apache version out of headers and error pages.

A friendly page while goodmail restarts

When the Node process is stopped or restarting, Apache answers 502/503/504. deploy/unavailable.html is a self-contained "We'll be right back" page in seven languages that retries every five seconds and opens the mailbox as soon as it answers. Copy it to a folder outside the web root that deploys replace, and add inside the HTTPS virtual host, before the general ProxyPass /:

Alias /goodmail-errors/ /var/www/goodmail/errors/
<Directory /var/www/goodmail/errors>
    Require all granted
</Directory>
ProxyPass /goodmail-errors/ !
ErrorDocument 502 /goodmail-errors/unavailable.html
ErrorDocument 503 /goodmail-errors/unavailable.html
ErrorDocument 504 /goodmail-errors/unavailable.html
Header always set Retry-After "10" "expr=%{REQUEST_STATUS} == 503"

The status stays a real 503 with a Retry-After header, so monitoring still sees the outage.

Updating

git pull
npm install
npm run build
sudo systemctl restart goodmail

Keep the previous dist/ and dist-server/ so you can roll back. Signed-in sessions survive a restart: the login cookie is sealed with the server key (secret.key), so goodmail rebuilds the session from it. Keep secret.key unchanged between versions.

Mail server side

goodmail needs nothing special from the mail server beyond IMAP and SMTP. Optional extras: ManageSieve for Mail Rules, and the helper commands for Passwords and Administration (ops/ in the repository).

Clone this wiki locally