Part 1: Local only / self-signed certificate


Running a Vaultwarden server with a self-signed certificate is actually quite simple and in summary, the following steps are required to achieve the goal of this part:

Nevertheless: This simplest expansion stage of the first part is NOT suitable for public operation. This is because access does not (yet) take place via an encrypted connection.

Time required: ca 30 Minuten.
Last updated:

  • 19.11.2022: Extension with diagram and minor adjustments.
  • 30.10.2022: Initial document.

Precondition for local operation

  • TrueNAS Core or pure FreeBSD server
    • A jail (e.g. named VAULT) is set up and started.
      • IP address of the VAULT jail is known (ex.
      • Hostname of the VAULT jail is known (ex. vault.domain.local) and reachable.
  • An SMTP account with a provider, so that emails can be sent.
    • Registration and a 2FA are handled via this.

Generally, such service should be set up in its own environment. This makes not only security, but also maintenance much easier. Jails can be backed up relatively easily and only the packages that are absolutely necessary are installed.


  • USERNAME = SSH User account (root Login ist nicht direkt möglich) with SU rights
  • VAULTIP = IP Adresse of the local Vaultwarden Server
  • VAULTHOSTNAME = Hostname of the local Vaultwarden Server
  • DATENBANKNAME = Name of Database on the PostgreSQL Server
  • DATENBANKBENUTZER = Name of the Vaultwarden user in PostgreSQL Server
  • DATENBANKKENNWORT = Password of the Vaultwarden user in PostgreSQL Server
  • ADMINTOKEN = Generated string to log in to the /admim area of the Vaultwarden server.


With this the setup looks locally like this:

                                                                  │ TrueNAS                     │
                                                                  │           │
                                                                  │ ┌─────────────────────────┐ │
                                                                  │ │ Vault                   │ │
                                                                  │ │       │ │
                                                                  │ │                         │ │
                                                                  │ │   postgresql13-server   │ │
                                                                  │ │       ▲                 │ │
                                                                  │ │       │                 │ │
                                                                  │ │   vaultwarden           │ │
                                                                  │ │       ▲                 │ │
                                                                  │ │       │                 │ │
                                                LAN: ─┼─┼─►   nginx               │ │
                                                                  │ └─────────────────────────┘ │

Vault Jail

Install packages

Login via SSH to the VAULT Jail: ssh USERNAME@VAULTIP oder ssh USERNAME@VAULTHOSTNAME in order to get root rights: su.

Deposit a newer FreeBSD package source, because otherwise FreeBSD always falls back to a relatively old package source.

mkdir -p /usr/local/etc/pkg/repos 
cp /etc/pkg/FreeBSD.conf /usr/local/etc/pkg/repos/FreeBSD.conf
sed -i '' 's/quarterly/latest/' /usr/local/etc/pkg/repos/FreeBSD.conf

Update package source: pkg update and install required packages: pkg install postgresql13-server vaultwarden nginx

Enable services and start them automatically whenever the jail is started:
sysrc vaultwarden_enable=YES
sysrc nginx_enable=YES
sysrc postgresql_enable=YES


Initialize database: /usr/local/etc/rc.d/postgresql initdb
Start PostgreSQL service: service postgresql start

Change and remember the following data if necessary:

DATABASEPASSWORD: PASSWORT (Please create something of your own and remember!)

In the following step we create the database user and the database.
This is done as a Postgres user: su - postgres:

createuser --pwprompt DATABASEUSER
createdb --encoding=UTF8 --locale=C --template=template0 --owner=DATABASEUSER DATABASENAME


Create Admin Token

The following command generates a random and long string: openssl rand -base64 48, which we will use as ADMINTOKEN (note well!). This can be used later to access the Admin Portal of Vaultwarden. So remember well!

Configration customize

Create initial Vault Goods configuration: cp /usr/local/etc/rc.conf.d/vaultwarden.sample /usr/local/etc/rc.conf.d/vaultwarden Since we are using PostreSQL, the DATABASE_URL must be filled in:


Note: Umlauts or special characters have to be translated into HTML code in the DATABASE KEYWORD.
For example, a ! is a %21, other characters are listed here.

With this we can now create the initial configuration of Vaultwarden: ee /usr/local/etc/rc.conf.d/vaultwarden

# Listening Adress local only





export SMTP_HOST

export SMTP_FROM

export SMTP_PORT

export SMTP_SSL



... and finally start the Vaultwarden service: service vaultwarden start


Create certificate (self-signed)

A NGINX web server (which can also be used as a proxy) should be installed in any case, so that a local access remains possible in case of need even if the call should actually be realized later via an OPNsense HAProxy. Here in the part is necessary in any case, because the Vaultwarden service is never directly addressed, but called through a proxy. This increases the security again enormously, because attacks do not go through directly to the Vaultwarden server and thus forms another hurdle. Hurdles are good!

The following command creates a self-signed certificate. This will generate a warning in the browser (once), but will ensure an encrypted connection.

openssl req -new -x509 -days 3650 -nodes -keyout /usr/local/etc/nginx/server.key -out /usr/local/etc/nginx/server.crt

Then OpenSSL will ask a few questions that want to be answered (at least the VAULTHOSTNAME should be adjusted)

Country Name (2 letter code) [AU]:
State or Province Name (full name) [Some-State]:
Locality Name (eg, city) []:
Organization Name (eg, company) [Internet Widgits Pty Ltd]:
Organization Name (eg, company) [Internet Widgits Pty Ltd]:
Organization Name (eg, company) [Internet Widgits Pty Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []: VAULTHOSTNAME
Email Address []:

The keys generated with it are still secured against prying eyes:
chmod 0400 /usr/local/etc/nginx/server.key && chmod 0400 /usr/local/etc/nginx/server.crt

Customize configuration

Everything is now stored in the NGINX configuration file: ee /usr/local/etc/nginx/nginx.conf

user www;
worker_processes 1;
events {
 worker_connections 1024;
http {
 include mime.types;
 default_type application/octet-stream;
 sendfile on;
 keepalive_timeout  65;
 gzip on;
 server_tokens off;
server {
 # HTTPS Umleitung
  listen 80;

  return 301 https://$host$request_uri;
  server {
 # HTTPS Auslieferung
  listen 443 ssl http2;
  location / {
   proxy_set_header X-Forwarded-For $remote_addr;
   proxy_set_header X-Forwarded-Proto $scheme;
   proxy_set_header Host $host;
  error_page 500 502 503 504 /50x.html;
  location = /50x.html {
   root /usr/local/www/nginx-dist;
  ssl_certificate /usr/local/etc/nginx/server.crt;
  ssl_certificate_key /usr/local/etc/nginx/server.key;

... and finally now also start the NGINX Webserver service: service nginx start

Call up Vaultwarden

Vaultwarden can then be reached at https://VAULTIP or https://VAULTHOSTNAME.
The admin console can be called with an /admin behind the used address (ADMINTOKEN!) and from now on all further settings will be done there as well.


Everyone (local) can now register on the server and store their own vaults and passwords. To do this, the email sending must work so that the email addresses can be verified.

So that the whole thing can also be used for cell phones and Co. from the road, is explained in part 2.