Install on a single host#
This documentation applies to Piler enterprise edition 2.3.0+
Revision #4
Publication date: 2026-NOV-02
From an empty Linux host to an archive that receives the journal of your mail server, with Docker. Every component runs on the one host: piler, piler-smtp, the web UI, MariaDB, Manticore, Tika, Valkey, the scanner, and Traefik for HTTPS with Let's Encrypt certificates.
Your license decides whether it's a single-tenant or a multi-tenant archive. For a larger installation (a master and workers) see large deployments; for an installation without Docker, installing piler enterprise edition.
1. What you need#
- A Linux host, amd64 or arm64, with Docker Engine and its compose plugin. Start with 2 vCPUs, 4 GB of memory and 200 GB of disk: the components take about 0.7 GB of memory idle with an empty archive, Tika, Manticore and piler need more while emails come in. For the disk, see sizing.
- The hostname in your license, e.g.
archive.example.com, with anA(andAAAA) record pointing to the host. piler-smtp accepts the emails sent to addresses at this hostname only. If your mail server delivers by DNS, anMXrecord of the hostname pointing to itself. - Port 443 reachable from the internet: Let's Encrypt checks it before it issues the certificate, and your users reach the archive on it.
- Port 25 reachable by your mail server: it sends the copies of the emails there.
- Your license file,
license.jwt.
2. Get the files#
git clone https://github.com/jsuto/piler-examples.git
cd piler-examples/examples/v2/single-node
Keep the directory: the settings and the keys of the installation are written into it.
3. Run the setup#
./setup.sh
It asks for:
- the path of your license file
- the hostname of the archive: the one in the license is offered, it warns about another one
- the hostnames of the tenants (multi-tenant license only; you can add them later)
- the email address Let's Encrypt sends certificate notices to
- the repository of the backups (you can leave it empty and set it later, see backup)
- the mail relay piler sends emails through: restored emails go back to the mailboxes through it
Then it writes .env (hostnames, random database and backup passwords,
image versions), env (the settings of piler and the web UI, with a random
JWT_SECRET and IMAP_ENCRYPT_KEY) and piler.key (the key the emails are
encrypted with), and creates the Docker volumes. It overwrites none of
these: if a step fails, fix the cause and run it again.
Back up piler.key, .env and env now, outside the host. Without
piler.key the archived emails can't be read, not even from a backup, and
without the BACKUP_PASSWORD in .env the backups can't be restored.
4. Start it#
docker compose up -d --wait
It returns when every component is healthy, in about half a minute, or says which one isn't. Let's Encrypt issues the certificate within a minute, if the hostname points to the host and port 443 is open.
5. First login#
Open https://archive.example.com/ and log in as admin@local with the
password pilerrocks. Change the password right away.
The administrator manages the archive but can't see emails. Then:
- Domain: add the email domains of your organization. Piler tells incoming, outgoing and internal emails apart by them, and users see the emails of their own addresses.
- Users > User: add an auditor (role Auditor) who can search the whole archive, or connect your directory, see LDAP authentication, Microsoft Entra ID, Google Workspace.
- Compliance > Retention Policy: how long emails are kept, by default and by rule. Check it against your legal requirements before the first emails arrive: the retention of an email is set when it's archived. The default retention (10 years) is changed on that page too (2.3.1+), see Retention and exclusion rules.
6. Test it#
Send an email to the archive from any host that can reach port 25, e.g. with swaks:
swaks --server archive.example.com --from anna@example.com \
--to journal@archive.example.com --header "Subject: archive test"
Log in as the auditor and search for archive test. The email shows up
within a few seconds.
7. Send the journal#
Have your mail server send a copy of every incoming and outgoing email to
an address at the hostname in your license, e.g.
journal@archive.example.com. With a multi-tenant license the address is
<tenant id>@archive.example.com.
- Microsoft 365: journaling
- Postfix, Axigen: mail server integration
- To archive the Bcc recipients too: envelope recipients
Allow port 25 only from your mail servers, with a firewall: anyone who can reach it can put emails into the archive. To archive the emails from before the journal, see import legacy emails.
Settings#
Don't edit docker-compose.yaml or traefik-dynamic.yaml: a newer version
of the example would replace your changes.
.env: the hostnames (PILER_HOSTNAME,TENANT_HOSTNAMES), the Let's Encrypt address and the image versions. Rundocker compose up -d --waitafter a change. A new tenant hostname gets its certificate then.env: everything else, see the configuration options. Rundocker compose up -d --force-recreate piler piler-ui scannerafter a change.
The database passwords in .env are used when the database is created,
changing them later doesn't change the database.
Backup#
piler-backup.sh backs up the installation into a
restic repository: the databases,
the search index, the stored emails and attachments, and the installation:
.env, the compose files and what the containers mount from its directory
(env, piler.key, the license). Other files of the directory are not backed
up. It runs in containers, there's nothing to install.
WORKDIR in .env (default backups/work) is where the database dump and
these files are put together. It needs room for the dump, which isn't
encrypted there until the backup removes it.
BACKUP_REPOSITORY in .env says where the backups go, the setup asked for
it:
s3:https://host/bucket/path, withAWS_ACCESS_KEY_IDandAWS_SECRET_ACCESS_KEYin.envsftp:user@host:/path, with root's SSH keyrest:https://host/path- a directory: an absolute path outside the directory of the installation, on another disk. A directory on the host's own disk is lost with the host.
BACKUP_PASSWORD in .env encrypts the backups, the setup generated it.
Keep a copy off the host: without it there's no restore.
Then, as root, in the directory of the installation:
../../../scripts/piler-backup.sh install-cron # every day at 02:30
../../../scripts/piler-backup.sh # a backup now
../../../scripts/piler-backup.sh snapshots # the backups
With a directory, create the repository first, with
BACKUP_INIT=1 ../../../scripts/piler-backup.sh. Later backups don't create
one: if the disk isn't mounted, the backup fails instead of filling the
host's disk.
install-cron takes another time too (install-cron 03:15). It writes
/etc/cron.d/piler-backup-<hostname>, the log is backups/backup.log;
without root or /etc/cron.d it prints a line for your crontab. A backup
doesn't start while another one runs, and it stops if MariaDB or piler isn't
running. Without Manticore it backs up the rest, and a restore of it needs a
reindex. Either way it's recorded as a failed backup, which the health page
and the alerts show.
Every backup stores only what's new since the last one. 7 daily, 4 weekly
and 12 monthly backups are kept (BACKUP_KEEP_DAILY, BACKUP_KEEP_WEEKLY,
BACKUP_KEEP_MONTHLY in .env). On Sundays the space of the removed
backups is freed, and 5% of the data is read back to check it. A purged or
erased email stays in the backups until they're removed, so set these with
your compliance rules in mind.
Admin > Health shows the last backup. It's red if the backup failed, yellow if it's older than 48 hours or the check failed, and grey until the first one.
Restore on a new host#
- Install Docker and get the files as in step 2. Don't run the setup.
- In the directory of the example, create
.envwithBACKUP_REPOSITORYandBACKUP_PASSWORD(and the S3 keys). - Pick a backup:
../../../scripts/piler-backup.sh snapshots ../../../scripts/piler-backup.sh restore <id>
It restores the settings, the key, the license and the image versions of the backup, then the databases, the emails and the search index, and starts piler. It only restores into an installation without piler's volumes, it never overwrites an archive. Then point the hostname to the new host.
The emails archived after the backup are not in it. Those your mail server couldn't deliver meanwhile arrive when the host is back.
../../../scripts/piler-backup.sh extract <id> <directory> extracts the
files of a backup without restoring them.
Upgrade#
Update the example (git pull in piler-examples), then in the
directory of the installation:
../../../scripts/piler-upgrade.sh # your versions, and the newer releases
../../../scripts/piler-upgrade.sh 2.2.6 # upgrade to 2.2.6
It shows the upgrade notes of every release since yours, and
asks before it does anything; a release with an upgrade step needs your
confirmation that it's done. Then it pulls the new images, dumps the
database and copies .env and env into backups/, stops piler (piler-smtp
keeps taking the email) and runs the migrations of the new version, sets the
new versions in .env, starts the new
containers and waits until they're healthy. If they don't get healthy, it prints how to go back.
--smtp <version> and --scanner <version> upgrade piler-smtp and the
scanner too, --yes doesn't ask, --no-backup skips the database dump.
Remove the old backups from backups/ yourself.
Troubleshooting#
docker compose psshows the health of each component,docker compose logs <component>its log.- No certificate (the browser warns about a "TRAEFIK DEFAULT CERT"): check
that the hostname resolves to the host and port 443 is reachable from the
internet, then
docker compose logs traefik. - The test email doesn't arrive:
docker compose logs piler-smtp piler. If piler couldn't archive it, it's in the error directory:docker compose exec piler ls /var/piler/error. - An installation set up before
setup.sh(2.3.0): see Existing installation in the README of the example.