Documentation Self-Hosting Guide

Self-Hosting Guide

Everything you need to deploy LastPost on your own server, configure it for your mail stack, and keep it running.

Download lastpost.php

Free to download. Free to keep. No account required.

Get LastPost

Server requirements

RequirementMinimumNotes
PHP8.18.2 or 8.3 recommended.
PHP extension: imapRequiredFor IMAP connections. Package name is usually php-imap on Debian/Ubuntu or php8x-imap on Alpine.
PHP extension: opensslRequiredFor SSL/TLS connections to your mail server. Enabled by default in most PHP builds.
PHP extension: sessionRequiredFor session-based auth. Enabled by default.
Web serverAnyApache, Nginx, Caddy, LiteSpeed — anything that can run PHP files.
HTTPSStrongly recommendedYour mail credentials pass through LastPost. Run it behind HTTPS. Let's Encrypt makes this free.
DatabaseNoneLastPost requires no database.

To check whether the imap extension is available on your server:

php -m | grep imap

If nothing is returned, install it. On Debian/Ubuntu:

apt install php-imap
systemctl restart php8.x-fpm   # adjust version number

Installation

  1. Download lastpost.php

    Use the download link from your email. The file is a single PHP script — everything is bundled inside it.

  2. Edit the configuration block

    Open the file in a text editor. The configuration constants are near the top, clearly marked:

    // ── Configuration — edit these to match your mail server ──────────────
    define('LP_IMAP_HOST', 'mail.example.com');
    define('LP_IMAP_PORT', 993);
    define('LP_SMTP_HOST', 'mail.example.com');
    define('LP_SMTP_PORT', 465);

    Set the hostname and ports for your mail server. See the configuration reference for all available constants.

  3. Upload the file

    Copy lastpost.php to your web server. Common locations:

    • /var/www/mail.example.com/lastpost.php — subdomain root
    • /var/www/html/mail/lastpost.php — path under existing site

    The file can live anywhere your web server can execute PHP.

  4. Set permissions

    LastPost needs write access to the directory it lives in for PHP session storage (if using file-based sessions, which is the default). The web server user (www-data on Debian, nginx on Fedora) should own the file:

    chown www-data:www-data /var/www/mail.example.com/lastpost.php
  5. Navigate to the URL

    Open your browser and go to the address where you deployed it. The login screen appears. Enter your IMAP credentials and you're in.

Configuration reference

All configuration is done by editing the define() constants near the top of lastpost.php. No separate config file is needed.

Required

ConstantDefaultDescription
LP_IMAP_HOST'mail.step41.com'Hostname of your IMAP server.
LP_IMAP_PORT993IMAP port. 993 = IMAPS (SSL). Some servers use 143 + STARTTLS.
LP_SMTP_HOST'mail.step41.com'Hostname of your SMTP server. Usually the same as IMAP.
LP_SMTP_PORT465SMTP port. 465 = SMTPS (SSL). Some servers use 587 + STARTTLS.

Optional — maintenance cron

LastPost can sweep expired sessions on a schedule via a simple GET request. This is only needed if you're running a shared instance and want automatic cleanup.

ConstantDefaultDescription
LP_CRON_SECRET''Set to a random token to enable the cron endpoint. Leave empty to disable.
LP_IMAP_USER''IMAP username for cron-triggered maintenance sweeps.
LP_IMAP_PASS''IMAP password for cron-triggered maintenance sweeps.

To call the cron endpoint:

GET /api/maintenance/sweep-expired?secret=YOUR_SECRET

Optional — Sieve (vacation auto-reply)

ConstantDefaultDescription
LP_SIEVE_HOST''ManageSieve hostname. Leave empty to disable vacation auto-reply UI.
LP_SIEVE_PORT4190ManageSieve port. 4190 is the standard.

Optional — CardDAV (contact sync)

ConstantDefaultDescription
LP_CARDDAV_URL''Full CardDAV addressbook URL. Example: https://cloud.example.com/remote.php/dav/addressbooks/users/admin/contacts/
LP_CARDDAV_USER''CardDAV username. Defaults to the user's IMAP credentials if empty.
LP_CARDDAV_PASS''CardDAV password. Defaults to the user's IMAP credentials if empty.

Apache setup

LastPost works on Apache without any configuration changes. By default it uses query-string routing (lastpost.php?_api=folders). For clean URLs (/api/folders), add an .htaccess file in the same directory as lastpost.php:

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ lastpost.php [QSA,L]

This requires AllowOverride All (or at minimum AllowOverride FileInfo) in your Apache virtual host. If you can't use .htaccess, add the rewrite rules directly to your vhost config and omit the .htaccess file.

A minimal virtual host for a dedicated subdomain:

<VirtualHost *:443>
    ServerName mail.example.com
    DocumentRoot /var/www/mail.example.com
    DirectoryIndex lastpost.php

    <Directory /var/www/mail.example.com>
        AllowOverride All
        Require all granted
    </Directory>

    SSLEngine on
    SSLCertificateFile    /etc/letsencrypt/live/mail.example.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/mail.example.com/privkey.pem
</VirtualHost>

Nginx setup

A minimal Nginx server block:

server {
    listen 443 ssl;
    server_name mail.example.com;

    root /var/www/mail.example.com;
    index lastpost.php;

    ssl_certificate     /etc/letsencrypt/live/mail.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/mail.example.com/privkey.pem;

    location / {
        try_files $uri $uri/ /lastpost.php?$query_string;
    }

    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/php8.x-fpm.sock;  # adjust version
    }
}

Mail server compatibility

LastPost works with any mail server that supports standard IMAP and SMTP over SSL. Tested and confirmed working:

  • Mailu — IMAP port 993, SMTP port 465. No custom configuration needed.
  • Mailcow — same ports. Works out of the box.
  • iRedMail — IMAP port 993, SMTP port 587 (STARTTLS). Set LP_SMTP_PORT to 587.
  • Fastmail — IMAP: imap.fastmail.com:993, SMTP: smtp.fastmail.com:465. Use an app password.
  • Gmail — IMAP: imap.gmail.com:993, SMTP: smtp.gmail.com:465. Requires enabling IMAP in Gmail settings and using an app password (2FA required).

The persona system (send-as identities) works as long as your SMTP server authorizes the From address. On Mailu and Mailcow, aliases you've configured on the server are automatically authorized. On hosted providers like Gmail and Fastmail, you'll need to add send-as addresses through their web interface first.

Vacation auto-reply (Sieve)

If your mail server supports ManageSieve (Mailu and Mailcow both do), LastPost can set up server-side vacation auto-replies from within the app.

To enable it, set LP_SIEVE_HOST to your mail server hostname:

define('LP_SIEVE_HOST', 'mail.example.com');
define('LP_SIEVE_PORT', 4190);  // 4190 is the standard; usually correct

Once enabled, a vacation auto-reply panel appears in Settings. You can set the reply message, subject, and how long to wait before re-replying to the same sender.

Contact sync (CardDAV)

LastPost can pull contacts from a CardDAV server to use for autocomplete in the compose To field.

Set LP_CARDDAV_URL to your addressbook URL. The format depends on your CardDAV server — consult its documentation for the correct path. A Nextcloud example:

define('LP_CARDDAV_URL', 'https://nextcloud.example.com/remote.php/dav/addressbooks/users/admin/contacts/');

If your CardDAV credentials are the same as your IMAP credentials, leave LP_CARDDAV_USER and LP_CARDDAV_PASS empty — LastPost will use the login credentials automatically.

Upgrading

Upgrading is the same as installing:

  1. Download the new version

    Get the new lastpost.php from your download link. Each download email contains a fresh link tied to your address.

  2. Copy your configuration

    Open both the old and new files. Copy the define() constants from the top of your old file into the same block in the new one.

  3. Replace the file on your server

    Upload the new lastpost.php, overwriting the old one. No database migrations, no build steps. Done.

User data (personas, themes, preferences) is stored in localStorage in the browser, so upgrading the server file doesn't touch any of it.

Troubleshooting

Login fails immediately

Confirm your IMAP hostname and port are correct in the configuration constants. Test connectivity from the server directly:

openssl s_client -connect mail.example.com:993

If that fails, the server can't reach your mail server — check firewall rules and DNS.

"PHP imap extension not found"

Install php-imap on your server and restart PHP-FPM. See Server requirements.

Blank page after login

Check your PHP error log. A common cause is the session directory not being writable by the web server user. Confirm /tmp or your configured session.save_path is writable by www-data (or your web server user).

Emails send but recipients see the wrong From name

The From address in your persona must be authorized by your SMTP server. If you send as [email protected] but your SMTP server only permits your primary address, the server may rewrite the From header or reject the message. Add the alias to your mail server's authorized senders list.

STARTTLS instead of SSL (port 587)

Some SMTP servers use STARTTLS on port 587 rather than implicit SSL on 465. Set LP_SMTP_PORT to 587. LastPost detects STARTTLS automatically when port 587 is configured.

Self-signed certificate on your mail server

PHP's imap extension validates SSL certificates by default. If your mail server uses a self-signed cert, IMAP connections will fail. Options: get a real certificate (Let's Encrypt), or pass the CURLOPT_SSL_VERIFYPEER flag — see the note in the configuration block at the top of lastpost.php.

Theme token reference

All 31 theme-controllable token names, for building custom themes:

Token keyCSS propertyControls
bg--lapo-bgMain app background
bg-surface--lapo-bg-surfaceCards, panels, sidebar
bg-raised--lapo-bg-raisedElevated surfaces, dropdowns
bg-input--lapo-bg-inputForm input backgrounds
bg-interactive--lapo-bg-interactiveHover state backgrounds
bg-overlay--lapo-bg-overlayModal/overlay backdrop
accent--lapo-accentPrimary brand color — buttons, links, highlights
accent-light--lapo-accent-lightLighter accent variant
accent-dark--lapo-accent-darkDarker accent variant
accent-glow--lapo-accent-glowAccent box-shadow glow
accent-ring--lapo-accent-ringFocus ring color
accent-tint--lapo-accent-tintLow-opacity accent fill
text--lapo-textPrimary body text
text-secondary--lapo-text-secondarySecondary / supporting text
text-muted--lapo-text-mutedMuted / placeholder text
text-dim--lapo-text-dimEven more subdued text
text-disabled--lapo-text-disabledDisabled state text
border--lapo-borderStandard border
border-strong--lapo-border-strongEmphasized border
border-subtle--lapo-border-subtleHairline / very subtle border
pass--lapo-passSuccess / positive status color
pass-bg--lapo-pass-bgSuccess background fill
pass-border--lapo-pass-borderSuccess border
warn--lapo-warnWarning status color
warn-bg--lapo-warn-bgWarning background fill
warn-border--lapo-warn-borderWarning border
fail--lapo-failError / destructive status color
fail-bg--lapo-fail-bgError background fill
fail-border--lapo-fail-borderError border
shadow-sm--lapo-shadow-smSmall shadow (cards)
shadow-md--lapo-shadow-mdMedium shadow (dropdowns, popovers)

Include a colors_light block alongside colors to support light mode. The keys are identical — light mode simply swaps to that block when active.