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.
Server requirements
| Requirement | Minimum | Notes |
|---|---|---|
| PHP | 8.1 | 8.2 or 8.3 recommended. |
PHP extension: imap | Required | For IMAP connections. Package name is usually php-imap on Debian/Ubuntu or php8x-imap on Alpine. |
PHP extension: openssl | Required | For SSL/TLS connections to your mail server. Enabled by default in most PHP builds. |
PHP extension: session | Required | For session-based auth. Enabled by default. |
| Web server | Any | Apache, Nginx, Caddy, LiteSpeed — anything that can run PHP files. |
| HTTPS | Strongly recommended | Your mail credentials pass through LastPost. Run it behind HTTPS. Let's Encrypt makes this free. |
| Database | None | LastPost 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
-
Download lastpost.php
Use the download link from your email. The file is a single PHP script — everything is bundled inside it.
-
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.
-
Upload the file
Copy
lastpost.phpto 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.
-
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-dataon Debian,nginxon Fedora) should own the file:chown www-data:www-data /var/www/mail.example.com/lastpost.php -
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
| Constant | Default | Description |
|---|---|---|
LP_IMAP_HOST | 'mail.step41.com' | Hostname of your IMAP server. |
LP_IMAP_PORT | 993 | IMAP 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_PORT | 465 | SMTP 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.
| Constant | Default | Description |
|---|---|---|
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)
| Constant | Default | Description |
|---|---|---|
LP_SIEVE_HOST | '' | ManageSieve hostname. Leave empty to disable vacation auto-reply UI. |
LP_SIEVE_PORT | 4190 | ManageSieve port. 4190 is the standard. |
Optional — CardDAV (contact sync)
| Constant | Default | Description |
|---|---|---|
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_PORTto 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:
Download the new version
Get the new
lastpost.phpfrom your download link. Each download email contains a fresh link tied to your address.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.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 key | CSS property | Controls |
|---|---|---|
bg | --lapo-bg | Main app background |
bg-surface | --lapo-bg-surface | Cards, panels, sidebar |
bg-raised | --lapo-bg-raised | Elevated surfaces, dropdowns |
bg-input | --lapo-bg-input | Form input backgrounds |
bg-interactive | --lapo-bg-interactive | Hover state backgrounds |
bg-overlay | --lapo-bg-overlay | Modal/overlay backdrop |
accent | --lapo-accent | Primary brand color — buttons, links, highlights |
accent-light | --lapo-accent-light | Lighter accent variant |
accent-dark | --lapo-accent-dark | Darker accent variant |
accent-glow | --lapo-accent-glow | Accent box-shadow glow |
accent-ring | --lapo-accent-ring | Focus ring color |
accent-tint | --lapo-accent-tint | Low-opacity accent fill |
text | --lapo-text | Primary body text |
text-secondary | --lapo-text-secondary | Secondary / supporting text |
text-muted | --lapo-text-muted | Muted / placeholder text |
text-dim | --lapo-text-dim | Even more subdued text |
text-disabled | --lapo-text-disabled | Disabled state text |
border | --lapo-border | Standard border |
border-strong | --lapo-border-strong | Emphasized border |
border-subtle | --lapo-border-subtle | Hairline / very subtle border |
pass | --lapo-pass | Success / positive status color |
pass-bg | --lapo-pass-bg | Success background fill |
pass-border | --lapo-pass-border | Success border |
warn | --lapo-warn | Warning status color |
warn-bg | --lapo-warn-bg | Warning background fill |
warn-border | --lapo-warn-border | Warning border |
fail | --lapo-fail | Error / destructive status color |
fail-bg | --lapo-fail-bg | Error background fill |
fail-border | --lapo-fail-border | Error border |
shadow-sm | --lapo-shadow-sm | Small shadow (cards) |
shadow-md | --lapo-shadow-md | Medium 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.