Files
self-host/CLAUDE.md

21 KiB

Bitwarden Self-Host - Claude Code Configuration

Bitwarden's self-host release repository — a deployment and release orchestration hub that aggregates versions from upstream repos, publishes Docker images to ghcr.io, and provides installation tooling for multiple deployment models.

Overview

What This Project Does

  • Primary function: Orchestrates releases and deployments for Bitwarden's self-hosted product, packaging upstream application code (bitwarden/server, bitwarden/clients, bitwarden/key-connector) into Docker images and cloud marketplace VM images.
  • Key interfaces: bitwarden.sh/bitwarden.ps1 (installation CLI), run.sh/run.ps1 (Docker Compose orchestration), GitHub Actions workflows (CI/CD), Bitwarden Lite Docker container (all-in-one deployment).
  • Target users: Self-hosted Bitwarden administrators, Bitwarden release engineers, cloud marketplace consumers.

Key Concepts

  • Traditional deployment: Multi-container Docker Compose setup orchestrated by shell/PowerShell scripts
  • Bitwarden Lite: Single all-in-one Docker container using supervisord to run all .NET services + Nginx
  • Marketplace images: Pre-built VM images (AMI, Azure SIG, DO droplet) for one-click cloud deployments via Packer
  • Upstream versions: This repo does not contain application source code — it tracks coreVersion (server), webVersion (clients), and keyConnectorVersion from upstream repos
  • Cosign signing: All container images are signed with keyless Cosign (OIDC via GitHub Actions) for supply chain integrity
  • Calendar versioning: Versions follow YYYY.MM.PATCH format (e.g., 2026.3.1)

Architecture & Patterns

System Architecture

Upstream Repos (bitwarden/server, bitwarden/clients, bitwarden/key-connector)
         |
         | version.json tracks upstream versions
         v
    Release Pipeline (release.yml)
         |
    ┌────┴────────────────────┬──────────────────────┐
    |                         |                      |
    v                         v                      v
 Traditional              Bitwarden Lite         Marketplace Images
 (14 Docker images        (single container)     (Packer-built VMs)
  pushed to ghcr.io)           |                      |
    |                         |              ┌────────┼────────┐
    v                         v              v        v        v
 bitwarden.sh/ps1     docker-compose.yml    AWS    Azure    DigitalOcean
 + run.sh/ps1         + settings.env        AMI    SIG      Droplet
 + Docker Compose     + entrypoint.sh

Code Organization

self-host/
├── .github/
│   ├── workflows/             # CI/CD pipelines
│   │   ├── release.yml        # Main release orchestration
│   │   ├── build-bitwarden-lite.yml  # Lite image build (reusable)
│   │   ├── release-aws.yml    # AWS Marketplace
│   │   ├── release-azure.yml  # Azure Marketplace
│   │   ├── release-digital-ocean.yml # DO Marketplace
│   │   ├── scan.yml           # SAST + Sonar
│   │   └── cleanup-container-images.yml  # Image cleanup
│   └── CODEOWNERS
├── bitwarden-lite/            # All-in-one container
│   ├── Dockerfile             # Multi-stage build (296 lines)
│   ├── docker-compose.yml     # Local development compose
│   ├── entrypoint.sh          # Container init (certs, DB, services)
│   ├── settings.env           # Configuration template
│   ├── .env.example           # Compose env vars
│   ├── supervisord/           # Process manager configs
│   │   ├── supervisord.conf
│   │   └── *.ini              # Per-service configs (admin, api, etc.)
│   ├── nginx/                 # Reverse proxy configs
│   └── hbs/                   # Handlebars templates for config generation
├── CommonMarketplace/         # Shared marketplace setup
│   ├── scripts/               # VM provisioning (UFW, cleanup, first-run)
│   └── files/                 # Installed files (MOTD, cron, cloud-init)
├── AWSMarketplace/            # AWS-specific Packer + validation
├── AzureMarketplace/          # Azure-specific Packer + validation
├── DigitalOceanMarketplace/   # DO-specific validation
├── bitwarden.sh               # Linux/macOS installation CLI
├── bitwarden.ps1              # Windows installation CLI
├── run.sh                     # Linux/macOS Docker Compose orchestrator
├── run.ps1                    # Windows Docker Compose orchestrator
└── version.json               # Upstream version manifest

Key Principles

  1. No application source code: This repo only packages and deploys — application logic lives in bitwarden/server and bitwarden/clients
  2. Platform parity: Bash and PowerShell scripts maintain feature parity (bitwarden.sh/bitwarden.ps1, run.sh/run.ps1)
  3. Supply chain security: All container images are Cosign-signed; marketplace images are validated with platform-specific check scripts
  4. Configuration-driven: Services are toggled via BW_ENABLE_* environment variables, not code changes

Core Patterns

Marketplace Image Build Pattern

Purpose: Consistent VM image creation across cloud providers using Packer with shared provisioning scripts.

Implementation: Each marketplace has a .pkr.hcl file that references CommonMarketplace/ for shared setup, then runs platform-specific validation.

CommonMarketplace/scripts/01-setup-first-run.sh  # Create user, set permissions
CommonMarketplace/scripts/02-ufw-bitwarden.sh    # Firewall rules
CommonMarketplace/scripts/90-cleanup.sh           # Sanitize image
{Platform}Marketplace/scripts/99-img-check.sh     # Platform-specific validation

Service Enablement Pattern

Purpose: Toggle individual .NET services in Bitwarden Lite without rebuilding the container.

Implementation (bitwarden-lite/entrypoint.sh):

# Services controlled by BW_ENABLE_* env vars in settings.env
# entrypoint.sh reads these and enables/disables supervisord .ini files
if [ "$BW_ENABLE_ADMIN" = "true" ]; then
    # Enable admin.ini in supervisord
fi

Version Tracking Pattern

Purpose: Single source of truth for upstream dependency versions.

Implementation (version.json):

{
  "coreVersion": "2026.3.1",
  "webVersion": "2026.3.0",
  "keyConnectorVersion": "2025.11.0"
}

The release workflow fetches latest versions from upstream repos, updates this file, and propagates versions to bitwarden.sh, bitwarden.ps1, and Docker image tags.


Development Guide

Adding a New Marketplace Platform

1. Create Platform Directory

{Platform}Marketplace/
├── marketplace-image.pkr.hcl    # Packer template
└── scripts/
    └── 99-img-check.sh          # Platform validation

2. Write Packer Template ({Platform}Marketplace/marketplace-image.pkr.hcl)

Reference CommonMarketplace/ for shared provisioning:

build {
  provisioner "shell" {
    scripts = [
      "${path.root}/../CommonMarketplace/scripts/01-setup-first-run.sh",
      "${path.root}/../CommonMarketplace/scripts/02-ufw-bitwarden.sh",
      "${path.root}/../CommonMarketplace/scripts/90-cleanup.sh",
      "${path.root}/scripts/99-img-check.sh"
    ]
  }
}

3. Write Validation Script (scripts/99-img-check.sh)

Check platform requirements: OS version, cloud-init, Docker, firewall, no SSH keys, no bash history, no logs.

4. Create Release Workflow (.github/workflows/release-{platform}.yml)

  • Triggered by release event or manual dispatch
  • Runs Packer build with platform credentials
  • Reference existing workflows (release-aws.yml, release-azure.yml) as templates

5. Update CODEOWNERS (.github/CODEOWNERS)

Add ownership entry for the new directory.

Adding a New Service to Bitwarden Lite

1. Add supervisord config (bitwarden-lite/supervisord/{service}.ini)

[program:{service}]
command=/app/{Service}/{Service}.dll
directory=/app/{Service}
autostart=true
autorestart=true
priority=20
stdout_logfile=/var/log/bitwarden/{service}.log
stdout_logfile_maxbytes=10MB
stdout_logfile_backups=5
redirect_stderr=true

2. Update Dockerfile (bitwarden-lite/Dockerfile)

Add build stage for the new .NET service and copy artifacts.

3. Update entrypoint.sh (bitwarden-lite/entrypoint.sh)

Add BW_ENABLE_{SERVICE} toggle logic.

4. Update settings.env (bitwarden-lite/settings.env)

Add the new BW_ENABLE_{SERVICE} variable with default value.

5. Update nginx config if the service needs HTTP routing (bitwarden-lite/nginx/).

Modifying Installation Scripts

When editing bitwarden.sh or bitwarden.ps1:

  • Keep both scripts in sync — changes to one usually require the same change to the other
  • Version strings are embedded directly in these scripts (updated by release.yml)
  • Test both docker compose (plugin) and docker-compose (standalone) code paths in bitwarden.sh

Data Models

Core Types

This repo has no application data models. The key data structure is version.json:

{
  "coreVersion": "YYYY.MM.PATCH",
  "webVersion": "YYYY.MM.PATCH",
  "keyConnectorVersion": "YYYY.MM.PATCH"
}

Configuration Schema

settings.env — flat key-value environment file consumed by docker-compose.yml and entrypoint.sh. No formal schema; validated at runtime by the application services.

Packer variables — defined in .pkr.hcl files with HCL variable blocks and sourced from environment or Azure Key Vault.


Security & Configuration

Security Rules

MANDATORY - These rules have no exceptions:

  1. Never hardcode secrets: All secrets flow through Azure Key Vault or GitHub Secrets — never commit credentials, API keys, or passwords to the repository
  2. Sign all container images: Every image pushed to ghcr.io must be signed with Cosign keyless signing. Verification: cosign verify ghcr.io/bitwarden/$IMAGE:latest --certificate-identity-regexp="https://github\.com/bitwarden/self-host/\.github/workflows/release\.yml@.*" --certificate-oidc-issuer="https://token.actions.githubusercontent.com"
  3. Sanitize marketplace images: All marketplace VM images must pass platform-specific validation (99-img-check.sh) ensuring no SSH keys, bash history, or logs remain
  4. Use parameterized inputs: Shell scripts must not interpolate untrusted input into commands — use proper quoting and parameterization
  5. Minimal container privileges: Bitwarden Lite runs services as a non-root user (configurable via PUID/PGID)

Security Functions

Function/Tool Purpose Usage
Cosign keyless signing Container image integrity Runs in release.yml after every image push
Anchore/Grype scanning Container vulnerability detection Runs in build-bitwarden-lite.yml, uploads SARIF
Checkmarx SAST Static application security testing Runs in scan.yml on every push/PR
UFW firewall setup VM network hardening CommonMarketplace/scripts/02-ufw-bitwarden.sh
Image validation scripts Marketplace image sanitization {Platform}Marketplace/scripts/99-img-check.sh

Environment Configuration

Required Variables (settings.env):

Variable Required Description Example
BW_DOMAIN Yes Server hostname bitwarden.example.com
BW_DB_PROVIDER Yes Database type mysql, postgresql, sqlserver, sqlite
BW_DB_SERVER Yes* Database host db
BW_DB_DATABASE Yes* Database name bitwarden_vault
BW_DB_USERNAME Yes* Database user bitwarden
BW_DB_PASSWORD Yes* Database password (set securely)
BW_INSTALLATION_ID Yes UUID from bitwarden.com/host UUID format
BW_INSTALLATION_KEY Yes License key (from bitwarden.com/host)

*Not required when BW_DB_PROVIDER=sqlite

Service Toggle Variables:

Variable Default Description
BW_ENABLE_ADMIN true Admin portal
BW_ENABLE_API true Core API
BW_ENABLE_EVENTS false Audit event logging
BW_ENABLE_ICONS true Icon cache service
BW_ENABLE_IDENTITY true SSO/OIDC provider
BW_ENABLE_NOTIFICATIONS true Real-time push notifications
BW_ENABLE_SCIM false SCIM directory provisioning
BW_ENABLE_SSO false SAML/OIDC authentication

SSL/TLS Variables:

Variable Default Description
BW_ENABLE_SSL false Enable HTTPS termination
BW_SSL_CERT Certificate filename
BW_SSL_KEY Private key filename
BW_ENABLE_SSL_CA false Enable custom CA
BW_SSL_CA_CERT CA certificate filename

CI/CD Secrets (Azure Key Vault):

Secret Purpose
BW-GHAPP-ID / BW-GHAPP-KEY GitHub App credentials for release automation
aws-selfhost-version-access-id / access-key S3 bucket access for version.json upload
aws-selfhost-version-bucket-name S3 bucket name
azure-marketplace-subscription-id Azure marketplace image builds

Authentication & Authorization

  • Container image signing: Cosign keyless via GitHub OIDC — no static keys, identity tied to workflow run
  • Marketplace VM access: SSH keys removed during image build; access provisioned by cloud platform on deployment
  • Self-signed certificates: entrypoint.sh generates RSA 4096-bit certs for Identity Server and optional SSL on first run
  • Random key generation: INTERNAL_IDENTITY_KEY, OIDC_IDENTITY_CLIENT_KEY, and DUO_AKEY are generated via openssl rand -hex 30 at container startup

Testing

Test Structure

This repository has no unit or integration tests. Quality assurance is handled through:

Testing Strategy:
├── SAST (Checkmarx)           # Static analysis via scan.yml
├── Code Quality (Sonar)       # Code quality via scan.yml
├── Container Scanning (Grype) # Vulnerability scanning in build-bitwarden-lite.yml
└── Image Validation           # Platform-specific 99-img-check.sh scripts

Running Tests

SAST + Sonar — triggered automatically on push/PR to main via scan.yml

Container vulnerability scanning — runs during Bitwarden Lite builds:

# Scanning is automated in CI, but can be run locally:
docker buildx build --platform linux/amd64 -t bitwarden/lite:local bitwarden-lite/
grype bitwarden/lite:local

Marketplace image validation — run platform check scripts during Packer build:

# Example: validate an AWS marketplace image
bash AWSMarketplace/scripts/99-img-check.sh

Test Environment

  • No local test environment setup required
  • CI runs in GitHub Actions with Ubuntu runners
  • Marketplace builds use platform-specific VMs (t3.small for AWS, Standard_B2s for Azure)

Code Style & Standards

Formatting

  • Indentation: 4 spaces for shell/PowerShell scripts, 2 spaces for JSON/YAML
  • Line endings: LF (Unix-style)
  • Encoding: UTF-8
  • Line length: 120-character guideline

Naming Conventions

  • kebab-case for: workflow files, Docker image names, directory names
  • camelCase for: JSON keys in version.json
  • SCREAMING_SNAKE_CASE for: environment variables (BW_DOMAIN, BW_ENABLE_*)
  • PascalCase for: .NET service names in Dockerfile (Admin, Api, Identity)

Imports

Not applicable — this repo contains shell scripts, Dockerfiles, and YAML workflows, not compiled source code.

Comments

  • Use # comments in shell scripts and YAML to explain non-obvious logic
  • Include inline comments in settings.env to document each variable group

Pre-commit Hooks

No pre-commit hooks configured. Quality gates run in CI:

  • Checkmarx SAST on push/PR
  • Sonar code quality on push/PR

Anti-Patterns

DO

  • Keep bitwarden.sh and bitwarden.ps1 in sync when making changes to either
  • Use CommonMarketplace/ for shared provisioning logic across cloud platforms
  • Sign all container images with Cosign in release workflows
  • Run platform-specific validation scripts (99-img-check.sh) in marketplace builds
  • Use Azure Key Vault for all CI/CD secrets
  • Test Docker builds locally with docker buildx build before pushing workflow changes
  • Update version.json through the release workflow, not manually
  • Match existing script style when editing shell scripts

DON'T

  • Commit secrets, API keys, or credentials to the repository
  • Manually edit version strings in bitwarden.sh/bitwarden.ps1 — the release workflow manages these
  • Skip Cosign signing when adding new container image pushes
  • Add application source code to this repo — it belongs in upstream repos
  • Remove or weaken marketplace image sanitization checks (SSH keys, bash history, logs)
  • Use docker-compose (v1) syntax without also supporting docker compose (v2 plugin) in scripts
  • Create marketplace images without running the corresponding 99-img-check.sh validation
  • Hardcode platform-specific logic in CommonMarketplace/ — use platform directories instead

Deployment

Building

Bitwarden Lite (local):

cd bitwarden-lite/
docker buildx build --platform linux/amd64 -t bitwarden/lite:local .

Running locally:

cd bitwarden-lite/
# 1. Copy and edit settings.env
# 2. Start services
docker-compose up -d
docker-compose logs -f bitwarden
docker-compose down

Traditional deployment:

./bitwarden.sh install   # Interactive setup (domain, SSL, DB)
./bitwarden.sh start
./bitwarden.sh stop
./bitwarden.sh restart
./bitwarden.sh update    # Pull latest images

Versioning

Calendar versioning: YYYY.MM.PATCH (e.g., 2026.3.1)

  • Tracks three upstream versions in version.json: coreVersion, webVersion, keyConnectorVersion
  • Release workflow auto-updates version strings across the repo

Publishing

Release is fully automated via release.yml (manual dispatch):

  1. Validates version not already released
  2. Fetches latest upstream versions, updates version.json and scripts
  3. Creates GitHub release with manifests
  4. Pushes 14 Docker images to ghcr.io/bitwarden with Cosign signing
  5. Uploads version.json to S3
  6. Triggers marketplace workflows (AWS, Azure, DigitalOcean)

Troubleshooting

Common Issues

Docker Compose Version Compatibility

Problem: Scripts fail because docker compose (v2 plugin) or docker-compose (v1 standalone) is not found.

Solution: bitwarden.sh includes fallback logic — ensure at least one is installed. Check with docker compose version or docker-compose --version.

Bitwarden Lite Services Not Starting

Problem: A service fails to start in the Lite container.

Solution: Check supervisord logs at /var/log/bitwarden/{service}.log. Verify the corresponding BW_ENABLE_* variable is set to true in settings.env. Inspect entrypoint behavior with docker-compose logs bitwarden.

Marketplace Image Validation Failures

Problem: 99-img-check.sh fails during Packer build.

Solution: Common failures include: leftover SSH keys (/root/.ssh/authorized_keys), uncleared bash history, remaining log files, or wrong OS version. Review the specific check that failed and fix the provisioning scripts in CommonMarketplace/scripts/.

SSL Certificate Issues

Problem: HTTPS not working in Bitwarden Lite.

Solution: Verify BW_ENABLE_SSL=true in settings.env and that certificate/key files are mounted at the expected paths. entrypoint.sh generates self-signed certs if none exist — check /etc/bitwarden/ssl/ for generated files.

Debug Tips

  • Bitwarden Lite logs: docker-compose logs -f bitwarden or inspect /var/log/bitwarden/ inside the container
  • Supervisord status: docker exec <container> supervisorctl status to check individual service states
  • Entrypoint debugging: Add set -x to entrypoint.sh for verbose shell tracing
  • Packer builds: Use PACKER_LOG=1 environment variable for verbose Packer output
  • Workflow debugging: Use act or check GitHub Actions run logs for CI/CD issues

References

Official Documentation

Internal Documentation

Tools & Libraries

Team Ownership

  • Default: @bitwarden/dept-shot (Self-Host team)
  • Docker/security files: Also @bitwarden/team-appsec
  • Release workflows: @bitwarden/dept-bre (Release Engineering)