> ## Documentation Index
> Fetch the complete documentation index at: https://docs.arct.cloud/llms.txt
> Use this file to discover all available pages before exploring further.

# Self-host Vaultwarden

> Install Vaultwarden on an Ubuntu VPS with Docker, HTTPS, restricted sign-ups, backups, and a safe update workflow.

Arct Cloud provides the unmanaged Linux VPS for this deployment. Vaultwarden is not preinstalled or managed by Arct Cloud, and you are responsible for the application, its data, security, and updates.

<Warning>
  A password vault is security-critical infrastructure. Use HTTPS, enable multi-factor authentication, keep tested off-server backups, and apply security updates promptly.
</Warning>

## Choose a Plan

Vaultwarden does not publish a fixed minimum server size. These are practical starting points for the container and its local SQLite database, not upstream requirements.

| Arct plan     | vCPU |  RAM | NVMe storage | Suggested use                                    |
| ------------- | ---: | ---: | -----------: | ------------------------------------------------ |
| **cvm.nano**  |    1 | 2 GB |        25 GB | Personal or small-family vault                   |
| **cvm.micro** |    2 | 4 GB |        40 GB | More users, attachments, or operational headroom |

Review current resources on the [Arct Cloud pricing page](https://www.arct.cloud/pricing) before deployment.

## Before You Begin

Prepare the following:

* A fresh Ubuntu 24.04 server
* A domain or subdomain such as `vault.example.com`
* An `A` record pointing that hostname to the server's public IPv4 address
* Docker Engine with the Docker Compose plugin, installed from the [official Docker repository](https://docs.docker.com/engine/install/ubuntu/)
* A reverse proxy such as Caddy or Nginx for HTTPS

Only ports `22`, `80`, and `443` need to be reachable publicly. Keep Vaultwarden's internal port private.

## Install Vaultwarden

<Steps>
  <Step title="Deploy and Connect">
    [Deploy an Ubuntu server](/compute/virtual-machines/deploy), then [connect over SSH](/compute/virtual-machines/connect-ssh).
  </Step>

  <Step title="Allow the Ubuntu User to Run Docker">
    After installing Docker Engine and the Compose plugin from Docker's official Ubuntu repository, add the default Ubuntu user to the Docker group:

    ```bash theme={null}
    sudo usermod -aG docker ubuntu
    exit
    ```

    Reconnect so the new group membership takes effect, then verify both commands work without `sudo`:

    ```bash theme={null}
    ssh ubuntu@YOUR_SERVER_IP
    docker version
    docker compose version
    ```

    <Warning>Membership in the `docker` group is effectively root access because it can start privileged containers and mount the host filesystem. Add only trusted administrator accounts.</Warning>
  </Step>

  <Step title="Create the Application Directory">
    ```bash theme={null}
    sudo install -d -m 0750 -o "$USER" -g "$USER" /opt/vaultwarden
    cd /opt/vaultwarden
    ```
  </Step>

  <Step title="Create the Compose File">
    Replace `vault.example.com` with your hostname.

    ```yaml compose.yaml theme={null}
    services:
      vaultwarden:
        image: vaultwarden/server:latest
        container_name: vaultwarden
        restart: unless-stopped
        environment:
          DOMAIN: "https://vault.example.com"
          SIGNUPS_ALLOWED: "true"
        volumes:
          - ./data:/data
        ports:
          - "127.0.0.1:8000:80"
    ```

    The bind mount keeps the vault database, keys, attachments, and configuration under `/opt/vaultwarden/data`.
  </Step>

  <Step title="Start Vaultwarden">
    ```bash theme={null}
    docker compose pull
    docker compose up -d
    docker compose ps
    ```

    Do not publish port `8000` through your cloud firewall or UFW.
  </Step>

  <Step title="Create the First Account Privately">
    Do not publish the proxy yet. Keep Vaultwarden bound to loopback and, from a second terminal on your local computer, create an SSH tunnel:

    ```bash theme={null}
    ssh -N -L 8000:127.0.0.1:8000 ubuntu@YOUR_SERVER_IP
    ```

    Open `http://127.0.0.1:8000`, create the first account, sign in, and enable authenticator-app (TOTP) two-step login before continuing. Save the recovery code somewhere separate from the vault.

    Back in the VPS session, close registration and force Compose to recreate the service with the new environment value:

    ```bash theme={null}
    cd /opt/vaultwarden
    sed -i 's/SIGNUPS_ALLOWED: "true"/SIGNUPS_ALLOWED: "false"/' compose.yaml
    docker compose up -d --force-recreate vaultwarden
    docker inspect vaultwarden --format '{{range .Config.Env}}{{println .}}{{end}}' \
      | grep -Fx 'SIGNUPS_ALLOWED=false'
    ```

    Stop the local tunnel with <kbd>Ctrl</kbd>+<kbd>C</kbd> only after the account, TOTP login, and disabled registration are verified. Leave the `/admin` page disabled unless you have a specific need for it. If you enable it, follow Vaultwarden's official guidance and use a hashed `ADMIN_TOKEN`.
  </Step>

  <Step title="Enable Public HTTPS">
    Install Caddy using its [official Debian and Ubuntu instructions](https://caddyserver.com/docs/install#debian-ubuntu-raspbian), then add this site block to `/etc/caddy/Caddyfile`:

    ```caddy theme={null}
    vault.example.com {
        reverse_proxy 127.0.0.1:8000
    }
    ```

    Validate and reload the configuration:

    ```bash theme={null}
    sudo caddy validate --config /etc/caddy/Caddyfile
    sudo systemctl reload caddy
    ```

    Caddy obtains and renews the TLS certificate after DNS resolves and ports `80` and `443` are reachable.

    Open `https://vault.example.com`, sign in, and confirm two-step login is enforced. Registration must already be disabled before the hostname becomes public.
  </Step>
</Steps>

## Firewall

Allow SSH before enabling UFW so you do not lock yourself out:

```bash theme={null}
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status
```

## Back Up and Restore

The `/data` directory is the authoritative persistent state. Stop the container before creating a filesystem archive so the SQLite database is consistent:

```bash theme={null}
(
  set -euo pipefail
  cd /opt/vaultwarden
  sudo install -d -m 0700 /var/backups/vaultwarden

  stamp="$(date -u +%Y%m%dT%H%M%SZ)"
  temporary="/var/backups/vaultwarden/.vaultwarden-${stamp}.tar.gz.tmp"
  archive="/var/backups/vaultwarden/vaultwarden-${stamp}.tar.gz"

  stopped=false
  restart_if_needed() {
    if [ "$stopped" = true ]; then
      if docker compose start vaultwarden; then
        stopped=false
      else
        echo 'CRITICAL: Vaultwarden did not restart; start it manually.' >&2
      fi
    fi
  }
  trap restart_if_needed EXIT
  trap 'exit 130' INT
  trap 'exit 143' TERM

  docker compose stop vaultwarden
  stopped=true
  sudo tar -czf "$temporary" data compose.yaml
  sudo tar -tzf "$temporary" >/dev/null
  sudo chmod 0600 "$temporary"
  sudo mv "$temporary" "$archive"

  docker compose start vaultwarden
  stopped=false
  trap - EXIT INT TERM
  printf 'Validated backup: %s\n' "$archive"
)
```

The timestamp is UTC with second-level precision. The archive is validated before an atomic rename on the same filesystem, and the exit trap restarts Vaultwarden if archive creation or validation fails. Copy the archive to encrypted storage outside the VPS and test restoration regularly. A backup left only on the same server does not protect against server or disk loss. Review the [official backup guidance](https://github.com/dani-garcia/vaultwarden/wiki/Backing-up-your-vault) before automating the process.

## Update Safely

Read the [Vaultwarden release notes](https://github.com/dani-garcia/vaultwarden/releases), create an off-server backup, then update:

```bash theme={null}
cd /opt/vaultwarden
docker compose pull
docker compose up -d
docker compose ps
docker compose logs --tail=100 vaultwarden
```

Do not downgrade a database unless the release notes explicitly document a supported rollback path. Restore a tested pre-update backup instead.

## Troubleshooting

| Symptom                                   | Check                                                                                                 |
| ----------------------------------------- | ----------------------------------------------------------------------------------------------------- |
| The web vault reports an insecure context | Confirm the public URL uses HTTPS and `DOMAIN` exactly matches it.                                    |
| Caddy cannot issue a certificate          | Verify DNS, ports `80` and `443`, and that no other service is using those ports.                     |
| New users can still register              | Confirm `SIGNUPS_ALLOWED` is `false`, then recreate the container with `docker compose up -d`.        |
| A client cannot connect                   | Check `docker compose ps`, Vaultwarden logs, proxy logs, and the configured server URL in the client. |

## Official Resources

<CardGroup cols={2}>
  <Card title="Vaultwarden Repository" icon="github" href="https://github.com/dani-garcia/vaultwarden">
    Official container examples, releases, security policy, and project documentation.
  </Card>

  <Card title="Vaultwarden Wiki" icon="book-open" href="https://github.com/dani-garcia/vaultwarden/wiki">
    Reverse proxy, backup, admin-page, and configuration guidance.
  </Card>
</CardGroup>

<Note>Vaultwarden is an unofficial Bitwarden-compatible server and is not associated with Bitwarden, Inc. Arct Cloud is an independent infrastructure provider and is not affiliated with, sponsored by, or endorsed by Vaultwarden or Bitwarden.</Note>
