Skip to content

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 an A (and AAAA) record pointing to the host. piler-smtp accepts the emails sent to addresses at this hostname only. If your mail server delivers by DNS, an MX record 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:

  1. 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.
  2. 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.
  3. 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.

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. Run docker compose up -d --wait after a change. A new tenant hostname gets its certificate then.
  • env: everything else, see the configuration options. Run docker compose up -d --force-recreate piler piler-ui scanner after 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, with AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY in .env
  • sftp:user@host:/path, with root's SSH key
  • rest: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#

  1. Install Docker and get the files as in step 2. Don't run the setup.
  2. In the directory of the example, create .env with BACKUP_REPOSITORY and BACKUP_PASSWORD (and the S3 keys).
  3. Pick a backup: ../../../scripts/piler-backup.sh snapshots
  4. ../../../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 ps shows 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.