Shared admin passwords usually live in a spreadsheet, a pinned chat message or the memory of one engineer. The weak ones get reused on every system a team touches. This may lead to data breaches as the credentials can be easily stolen and cause threats to businesses, which is why many organizations deploy an enterprise password manager to store and share credentials securely. Passbolt password manager is the self-hosted, open source answer: its browser extension encrypts every secret with OpenPGP before it leaves your machine, so the server only stores ciphertext.
This guide installs Passbolt CE from the official apt repository on Ubuntu and serves it over HTTPS with nginx. It then covers the web installer and browser extension, shares a credential with a second user and backs up the server keys. A fresh install of Passbolt 5.17 (tested October 2026 on Ubuntu 26.04, package steps repeated on 24.04) pulled 76 packages and passed the healthcheck with zero failures. For containers, the Passbolt Docker setup covers that path.
What the Passbolt package installs on each Ubuntu LTS
The same passbolt-ce-server package installs on both LTS releases, but it pulls a different database and PHP stack on each, because it only recommends default-mysql-server and lets Ubuntu decide what that means. These numbers come from the apt summary and the service cgroups on the two lab hosts:
| Measured on the test box | Ubuntu 26.04 LTS | Ubuntu 24.04 LTS |
|---|---|---|
| Passbolt CE package | 5.17.0-1 | 5.17.0-1 |
| Database pulled in | MySQL 8.4.11 | MySQL 8.0.46 |
| PHP-FPM | 8.5.4 | 8.3.6 |
| nginx | 1.28.3 | 1.24.0 |
| Packages installed | 76 | 87 |
| Download / disk added | 54.2 MB / 322 MB | 65.3 MB / 417 MB |
| Repository suite used | focal | focal |
| Memory after setup (MySQL, PHP-FPM, nginx) | 541 MB, 78 MB, 5 MB | not measured |
Two rows matter more than the rest. The Passbolt documentation says the package installs MariaDB. On Ubuntu the recommended database resolves to Oracle MySQL instead, so you get MySQL 8.4 on the newer release and 8.0 on the older one. MySQL 8.4 passed every database check in the healthcheck on 26.04, and on 24.04 the package created its MySQL 8.0 database cleanly. The MySQL on Ubuntu guide covers tuning that server if your vault grows. The memory figures are cgroup totals read with systemctl show -p MemoryCurrent after two users and one shared password existed, and MySQL dominates them: about 620 MB for the whole stack, which fits comfortably inside the 2 GB that Passbolt recommends. The 24.04 host was a minimal LXC container, so some of its 11 extra packages are libraries a VM image already ships. The MeCab dictionary packages pulled in by MySQL 8.0 and a separate PHP opcache package account for the rest.
Prerequisites
Passbolt’s install scripts rewrite the nginx and database configuration, so start from a server that runs nothing else. You need:
- Server: a fresh Ubuntu 26.04 or 24.04 server with sudo access. Passbolt recommends 2 CPU cores and 2 GB of RAM; our lab VM ran 2 vCPU and 4 GB, which is a floor for following along, not a sizing target for a large team.
- Domain: a hostname such as
passbolt.example.comwith an A record pointing at the server. Any DNS provider works. - Ports: TCP 80 and 443 reachable from the internet if you want the automatic certificate, plus 22 for SSH.
- Time sync: a working NTP client. The Passbolt login handshake signs messages with timestamps, and the healthcheck fails when the clock drifts. Ubuntu 26.04 runs
chronyout of the box and 24.04 runssystemd-timesyncd, so a stock install already has one. - SMTP (optional): a mail relay for user invitations. The core install works without one, and Step 8 adds it later.
Sizing beyond that is driven by the number of users and stored secrets, which land in MySQL. A team of a few hundred people is still a small database, so add RAM for the MySQL buffer pool before you add CPU.
Step 1: Set the domain and open the firewall
Export the domain once at the top of your SSH session, because the HTTPS check in Step 4 uses it. Replace the value with the real hostname:
export PASSBOLT_DOMAIN="passbolt.example.com"
echo "Passbolt domain: ${PASSBOLT_DOMAIN}"
Run the export again if you reconnect, since the variable only lives in the current shell. Next, open the firewall before installing anything. Port 80 has to be reachable during the package install itself, because the automatic TLS option requests the certificate from inside the installer. The nginx UFW profile does not exist yet at this point, so allow the ports by number:
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw --force enable
Allow SSH before you enable UFW, or the enable step cuts off your own session. Cloud servers usually have a second firewall (a security group or the provider’s firewall panel), and ports 80 and 443 need to be open there too. The UFW firewall guide covers rate limiting and logging if you want more than these three rules.
Step 2: Add the Passbolt repository
Passbolt ships a script that installs the repository key, the apt source, nginx and certbot, and publishes a SHA512 checksum for it on GitHub. Download both:
curl -LO https://download.passbolt.com/ce/installer/passbolt-repo-setup.ce.sh
curl -LO https://github.com/passbolt/passbolt-dep-scripts/releases/latest/download/passbolt-ce-SHA512SUM.txt
Verify the checksum and run the script only if it matches. This is the exact one-liner from the Passbolt documentation:
sha512sum -c passbolt-ce-SHA512SUM.txt && sudo bash ./passbolt-repo-setup.ce.sh || echo "Bad checksum. Aborting" && rm -f passbolt-repo-setup.ce.sh
A good run prints passbolt-repo-setup.ce.sh: OK first and ends with the Passbolt ASCII banner and “passbolt repository setup is finished”. Run it from an interactive SSH session: from a script or a non-interactive ssh host 'command' call, the final clear in the setup script fails and the one-liner prints “Bad checksum. Aborting” even though the checksum passed (see the errors section at the end). Confirm the source it wrote:
cat /etc/apt/sources.list.d/passbolt.sources
Expect a deb822 source with a signed-by keyring:
Types: deb
URIs: https://download.passbolt.com/ce/ubuntu
Suites: focal
Components: stable
Signed-By: /usr/share/keyrings/passbolt-repository.gpg
A focal suite looks wrong on a newer Ubuntu, but the script writes it on every release we tested. The Passbolt package is architecture-independent PHP, and its PHP, MySQL and nginx dependencies still come from your own Ubuntu archive.
Step 3: Choose how nginx gets its TLS certificate
Decide this before the install, because the package asks during installation and the manual option needs the certificate files to exist already. The installer offers three answers:
- auto: the package runs certbot with the nginx plugin and gets a Let’s Encrypt certificate over HTTP-01. Pick this for any server with a public IP, a DNS record that already resolves and port 80 open.
- manual: you point the installer at an existing fullchain PEM and private key. Pick this on a private network, behind NAT, or when you already issue certificates from an internal CA or with a DNS-01 client.
- none: plain HTTP only. Skip it: switching an instance from HTTP to HTTPS later unlinks every user’s browser extension.
Our lab server sits on a private LAN with no inbound port 80, so we used the manual option with a certificate from a lab CA. The auto option was not run in our lab. It is the same certbot HTTP-01 flow described in the Let’s Encrypt certificate guide, triggered from the package. For the manual option, copy your files into place first. We used these paths:
sudo mkdir -p /etc/ssl/passbolt
sudo cp fullchain.pem /etc/ssl/passbolt/fullchain.pem
sudo install -m 600 privkey.pem /etc/ssl/passbolt/privkey.pem
One behaviour to plan for with manual certificates: the installer copies both files into /etc/ssl/certs/<timestamp>-fullchain.pem and /etc/ssl/private/<timestamp>-privkey.pem and points nginx at the copies. A renewed certificate at your original path is never picked up. Either re-run sudo dpkg-reconfigure passbolt-ce-server after each renewal (No to the database question, Yes to nginx, manual again with the renewed certificate and key paths, then sudo systemctl reload nginx), or edit the two ssl_certificate lines in /etc/passbolt/nginx-ssl.conf to point at the live files and reload nginx from your renewal hook.
An internal CA needs one more step. The server has to trust that CA itself and resolve its own hostname, because Passbolt and the curl check below connect back to https://passbolt.example.com. Without that, curl and the SSL checks in the healthcheck fail even though every browser that trusts the CA works fine. Our lab trusted its CA like this and mapped the name in /etc/hosts:
sudo cp lab-ca.crt /usr/local/share/ca-certificates/lab-ca.crt
sudo update-ca-certificates
With auto, a few things decide whether it works on the first try. We did not run this path in the lab, so this list comes from the package scripts and the Passbolt TLS guide rather than a capture:
- Contact email: the certificate email prompt is pre-filled with
[email protected]. Replace it with an address you read. - Failed certificate request: if certbot fails (DNS not propagated, port 80 blocked), the package is left half-configured. Fix the cause, then run
sudo dpkg --configure -a. It asks every question again: answer No to the database question, since the database already exists (or reuse the exact same password), then pick auto again. - Renewal: keep port 80 open after the install, because renewals repeat the HTTP-01 check.
sudo certbot renew --dry-runproves renewal works before the first real one is due.
Step 4: Install passbolt-ce-server and answer the prompts
Install the package. On Ubuntu 26.04 this pulled 76 more packages, including MySQL and PHP-FPM (nginx and certbot already came with the repository script in Step 2):
sudo apt install passbolt-ce-server
Before unpacking anything, debconf asks a series of questions. Two of them are Yes/No dialogs (create the database, configure nginx), and on both releases the cursor starts on <No>. Press the left arrow (or Tab) to select <Yes> before Enter. Pressing Enter straight away skips the database and nginx setup completely, which is what happened on our first run. The full sequence, in order:
- Create a passbolt database on the local mysql? Select Yes.
- MySQL administrator username: keep
root. - MySQL administrator password: leave it empty. Root on a fresh Ubuntu MySQL authenticates with
auth_socket, and the installer runs as root, so no password is needed. - Passbolt database user: keep
passboltadmin, then enter and repeat a strong password for it. Write it down; the web installer asks for it again. Avoid quote characters in it, because the package pastes the password straight into a SQL statement. - Passbolt database name: keep
passboltdb. - Do you want to configure the nginx server? Select Yes.
- Passbolt nginx setup: choose
autoormanualfrom Step 3. This list starts onnone, so move the highlight with the arrow keys before Enter, or you end up with an HTTP-only site. - Passbolt domain name: your hostname, for example
passbolt.example.com. With auto, the next prompt asks for the Let’s Encrypt contact email; with manual, the next two ask for the certificate and key paths.
Read the TLS question carefully, since it decides how users reach the server:

When the package finishes it prints “Well done! Visit https://passbolt.example.com to finish installation”. Check that nginx now answers on HTTPS and sends you to the installer:
curl -s -o /dev/null -w "%{http_code} %{redirect_url}\n" "https://${PASSBOLT_DOMAIN}/"
A 302 to /install means TLS and PHP-FPM are in place and Passbolt is waiting for its installer:
302 https://passbolt.example.com/install
If curl cannot connect at all and you reached this point through dpkg-reconfigure rather than a fresh install, nginx is not listening on 443 yet, and the troubleshooting section shows the one-line fix. The stock Ubuntu nginx default site is also still enabled, so anyone browsing to the server’s IP address gets the “Welcome to nginx!” page instead of Passbolt. Remove it:
sudo rm /etc/nginx/sites-enabled/default
sudo nginx -t && sudo systemctl reload nginx
Now that nginx is installed, you can swap the two port rules from Step 1 for the named Nginx Full UFW profile, which opens the same two ports:
sudo ufw allow 'Nginx Full'
sudo ufw delete allow 80/tcp
sudo ufw delete allow 443/tcp
Afterwards sudo ufw status should list only OpenSSH and Nginx Full, each with its v6 twin.
Step 5: Run the Passbolt web installer
Open https://passbolt.example.com in the browser that will run the extension, click Get Started, and the installer starts with a system check. All three lines must be green before you continue. A red line here usually means the clock is not synced or the site is being reached over plain HTTP.

Click Start configuration. The database step wants the credentials you typed into the package dialogs: host localhost, port 3306, user passboltadmin, the password you chose and database passboltdb. Leave the connection type on mysql:// even though the server is MySQL rather than MariaDB.

Next comes the server OpenPGP key, which Passbolt uses to sign its side of every login. Enter a server name and an email address, keep the default RSA 3072-bit key and click Next. The key was generated and saved in 3.5 seconds on our VM.

On the options page, your https:// URL should already be the full base URL with Force SSL set to Yes. Keep both. With Force SSL on, Passbolt itself redirects plain HTTP requests to HTTPS with a 302, so you do not need a separate nginx redirect block.

Next, the emails page asks for SMTP details. If you have a relay, enter it and use Send test email. If you do not, enter a sender name and address plus localhost, port 25, no TLS and no authentication, then click Next. The installer accepts the form even when the test fails, and only invitations and notifications need working mail. Step 8 fixes it from the admin panel. The next page creates the first administrator. Use a real address you control, because that is your login name.

Click Skip on the subscription key page (that is for Passbolt Pro). The installation step took 27 seconds on our server, after which the browser lands on a setup URL for the admin account.
Step 6: Set up the admin account in the browser extension
Passbolt does all encryption in its browser extension, so the setup page stops until the extension is present. The Download extension button links to the store for your browser (Passbolt publishes the extension for Chrome, Firefox, Edge and Safari). Install it, then click Refresh to detect extension:

After the refresh, the extension takes over the page and asks for a passphrase. This passphrase unlocks the private key that decrypts every secret you will ever store, and the server cannot reset it, so use a long phrase you can type from memory. The quality bar should read “Very strong” before you continue.

After that, the extension generates your key and starts downloading the recovery kit, a file holding your private key. Treat this step as the lockout warning it is: if you lose both the browser profile and the recovery kit, your encrypted passwords are gone, and no administrator command can bring them back. Store the kit in a second location (an offline USB key or a sealed backup) before ticking the box.

The last step picks a color and a three-character security token. Passbolt shows that token every time it asks for your passphrase, so a fake prompt drawn by a phishing page will not have it.

Click Next and you land in an empty password workspace, logged in as the administrator, with the extension holding your decrypted key for the session.
Step 7: Store and share a password in Passbolt
Folders keep shared credentials organised by team or system, and sharing a folder applies its permissions to everything inside it. We created an Infrastructure folder from Create > Folder, opened it, and used Create > Password to add a Grafana admin login with a generated password. Saving a new secret asks for your passphrase, and the prompt carries the security token from Step 6:

Once saved, the password shows up in the folder with the secret masked. Copying it, revealing it or using autofill each asks the extension to decrypt it locally.

Sharing needs a second user who has finished their own extension setup, because Passbolt re-encrypts the secret with each recipient’s public key. With working mail (Step 8), add people from Users & Groups > Create > User and they get the setup link by email, which is how we added Amina ([email protected]). Without SMTP, register the user from the shell instead, which prints the same link for you to hand over:
sudo -u www-data /usr/share/php/passbolt/bin/cake passbolt register_user -u [email protected] -f Joseph -l Mwangi -r user
Hand over the one-time setup URL on the last line:
User saved successfully.
To start registration follow the link provided in your mailbox or here:
https://passbolt.example.com/setup/start/f23d8819-9eae-4945-9926-8bb3f313780c/f2e5571b-f97a-408a-a399-b052206011a2
The new user opens it in their own browser and goes through the same passphrase, recovery kit and security token screens as Step 6. Once they finish, select the password, click the share icon, type their name and pick a permission. The three levels are can read, can update and is owner, and read is the safe default for credentials people only need to use.

After Save and one more passphrase prompt, the Grafana password appeared in Amina’s vault on the next page load, with no manual sync.

If a user moves to a new laptop and still has their recovery kit, an administrator can issue a recovery link from the server instead of deleting and re-inviting the account:
sudo -u www-data /usr/share/php/passbolt/bin/cake passbolt recover_user -u [email protected] -c
The command prints a one-time URL, and the extension on the new machine asks for the recovery kit and passphrase:
The recovery token has been created.
The user [email protected] can recover its account here:
https://passbolt.example.com/setup/recover/start/28187d81-6f53-49f3-9ff9-690de0c5cf64/874e1763-c43e-48df-82dc-37c797760682
That link restores access to an existing key. It cannot replace a lost one, which is why the recovery kit warning in Step 6 matters.
Step 8: Configure email for invitations (optional)
Skip this step if you are happy handing out setup links from register_user. Everything up to Step 7 works without mail, but invitations from the UI and share notifications need a working relay. Open the gear icon and go to Organisation Settings > Emails > Email Server. Pick your provider (AWS SES, Google Workspace, Office 365, Mailgun, SendGrid and others) or Other, then fill in the host, port, TLS setting and credentials:

Click Save, then Send test email. In the lab we pointed Passbolt at a local Mailpit catcher on port 1025 instead of a real relay, and the test message arrived immediately. Invitations do not go out instantly, though: Passbolt queues them and a cron job installed by the package (/etc/cron.d/passbolt-ce-server) sends the queue once a minute. Our invitation left the queue 52 seconds after the user was created.
Once mail works, close the hole the healthcheck flags as “The SMTP Settings plugin endpoints are enabled”. Those endpoints let an administrator session read and change the SMTP credentials through the API, which you no longer need once mail is configured. Keep a copy of the Passbolt config file, then open it:
sudo cp -p /etc/passbolt/passbolt.php /etc/passbolt/passbolt.php.bak
sudo vim /etc/passbolt/passbolt.php
Add the security block after the existing ssl block inside the passbolt array. The installer writes the closing ] of the ssl block without a trailing comma, so add one there, and the section reads:
'ssl' => [
'force' => true,
],
'security' => [
'smtpSettings' => [
'endpointsDisabled' => true,
],
],
],
];
The file is read-only (mode 0440, owned by www-data), so save with :wq! as root. PHP-FPM reads it on every request, which means a typo takes the site down the moment you save. Check the syntax straight away, and copy the .bak file back if it fails:
sudo php -l /etc/passbolt/passbolt.php
PHP should answer No syntax errors detected in /etc/passbolt/passbolt.php. From now on the Email Server page shows “SMTP settings endpoints disabled.” instead of the form, so remove the block again if you ever need to change the relay from the UI.
Step 9: Verify the server with the Passbolt healthcheck
About 70 checks run across PHP, GPG, TLS, the database and the application config, so the healthcheck catches most misconfigurations before a user does. Run it as the web server user:
sudo -u www-data /usr/share/php/passbolt/bin/cake passbolt healthcheck
Our server finished with 65 PASS, 3 WARN and no FAIL before the SMTP change, and with the SMTP warning gone afterwards. The summary we kept an eye on:

The two warnings left are judgment calls rather than faults. “Host availability checking is disabled” means Passbolt does not verify the MX record of new users’ email domains. Turning on passbolt.email.validate.mx (an 'email' => ['validate' => ['mx' => true]] entry in the passbolt array) makes Passbolt look up the domain on every email validation and reject domains that have neither an MX nor an A record, so only enable it on a server with dependable outbound DNS. “Some email notifications are disabled by the administrator” reflects the default notification settings, which you can review under Emails > Email Notifications. Any FAIL line, on the other hand, needs fixing before you invite users.
Step 10: Back up the server keys and the database
A Passbolt restore needs three things: the database, the server OpenPGP key pair in /etc/passbolt/gpg, and /etc/passbolt/passbolt.php, which holds the key fingerprint and database credentials. A database dump without the server key cannot be restored into a working instance. Export the database with the built-in Passbolt command, which leaves out session and token data:
sudo -u www-data /usr/share/php/passbolt/bin/cake passbolt sql_export
Passbolt reports where the dump went. On our server it was a 170 KB file:
Saving backup file: /var/lib/passbolt/tmp/cache/database/backup_1792580538.sql
Excluding data of table(s): sessions, authentication_tokens.
Success: the database was saved on file!
Bundle the dump with the keys and config into one archive under /root:
sudo mkdir -p /root/passbolt-backup
sudo tar czf /root/passbolt-backup/passbolt-$(date +%F).tgz /etc/passbolt/gpg /etc/passbolt/passbolt.php /var/lib/passbolt/tmp/cache/database/backup_*.sql
Our archive came to 44 KB with two users. The SQL dump is written world-readable (mode 0644), so delete it once the archive exists and copy the archive off the server, ideally encrypted, because it contains the server’s private key:
sudo rm /var/lib/passbolt/tmp/cache/database/backup_*.sql
Users’ own private keys are never on the server, which is the point of the design: their recovery kits are their backup. Passbolt documents the full restore order in its official Ubuntu install and hosting docs, and the release notes are worth reading before each upgrade. The current release added passkeys as an MFA option. If you are still choosing between self-hosted managers, Vaultwarden on Ubuntu is the lighter Bitwarden-compatible alternative.
Errors we hit and how to fix them
Every message below came from the lab runs for this guide, on one of the two Ubuntu releases.
“Bad checksum. Aborting” right after “passbolt-repo-setup.ce.sh: OK”
The checksum was fine. We ran the one-liner through a non-interactive SSH command, the setup script finished, and then its final clear failed with 'unknown': I need something more specific. because there was no terminal. That non-zero exit triggers the || echo "Bad checksum. Aborting" branch. The repository is installed anyway. Confirm with apt-cache policy passbolt-ce-server, which showed a 5.17.0-1 candidate for us. Run the script from an interactive session to avoid the false alarm.
Pressing Enter skipped the database and nginx setup
Both Yes/No dialogs default to <No>, so Enter records passbolt/mysql-configuration: false and passbolt/nginx-configuration: false in debconf. The package then installs with no database and an HTTP-only site. Nothing is lost: run sudo dpkg-reconfigure passbolt-ce-server and answer the same questions again, selecting <Yes> with the arrow key.
HTTPS does not answer after dpkg-reconfigure
After the reconfigure, the new TLS include was on disk and nginx -t passed, but nothing listened on port 443 until nginx was reloaded. A fresh install did not need this. Reload it, as the Passbolt docs also suggest:
sudo systemctl reload nginx
Then repeat the curl check from Step 4. It should return the 302 to /install.
Email could not be sent: stream_socket_client(): Unable to connect to tcp://localhost:25 (Connection refused)
You get this from the web installer’s test email when no mail server listens on the host and port you entered. It does not block the installer, and the form keeps your values:

Point the settings at a real relay (port 587 with TLS for most providers) in Step 8. If the error names your relay rather than localhost, check that the server can reach it at all, since many cloud providers block outbound port 25.
“Please install the browser extension” with the extension installed
The setup page checks for the extension when it loads, so a page opened before the extension was installed keeps showing the download prompt. Click Refresh to detect extension or reload the tab. If it still does not appear, make sure the extension is enabled for the site and that you are on the https:// URL from the installer, not the server IP.
“SMTP settings endpoints disabled.” on the Email Server page
This appears after the endpointsDisabled change from Step 8 and means the hardening worked. To edit the relay again, remove the security block from /etc/passbolt/passbolt.php, make the change in the UI, and put the block back.