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), andkeyConnectorVersionfrom 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.PATCHformat (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
- No application source code: This repo only packages and deploys — application logic lives in
bitwarden/serverandbitwarden/clients - Platform parity: Bash and PowerShell scripts maintain feature parity (
bitwarden.sh/bitwarden.ps1,run.sh/run.ps1) - Supply chain security: All container images are Cosign-signed; marketplace images are validated with platform-specific check scripts
- 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) anddocker-compose(standalone) code paths inbitwarden.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:
- Never hardcode secrets: All secrets flow through Azure Key Vault or GitHub Secrets — never commit credentials, API keys, or passwords to the repository
- Sign all container images: Every image pushed to
ghcr.iomust 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" - 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 - Use parameterized inputs: Shell scripts must not interpolate untrusted input into commands — use proper quoting and parameterization
- 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.shgenerates RSA 4096-bit certs for Identity Server and optional SSL on first run - Random key generation:
INTERNAL_IDENTITY_KEY,OIDC_IDENTITY_CLIENT_KEY, andDUO_AKEYare generated viaopenssl rand -hex 30at 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-casefor: workflow files, Docker image names, directory namescamelCasefor: JSON keys inversion.jsonSCREAMING_SNAKE_CASEfor: environment variables (BW_DOMAIN,BW_ENABLE_*)PascalCasefor: .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.envto 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.shandbitwarden.ps1in 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 buildbefore pushing workflow changes - Update
version.jsonthrough 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 supportingdocker compose(v2 plugin) in scripts - Create marketplace images without running the corresponding
99-img-check.shvalidation - 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):
- Validates version not already released
- Fetches latest upstream versions, updates
version.jsonand scripts - Creates GitHub release with manifests
- Pushes 14 Docker images to
ghcr.io/bitwardenwith Cosign signing - Uploads
version.jsonto S3 - 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 bitwardenor inspect/var/log/bitwarden/inside the container - Supervisord status:
docker exec <container> supervisorctl statusto check individual service states - Entrypoint debugging: Add
set -xtoentrypoint.shfor verbose shell tracing - Packer builds: Use
PACKER_LOG=1environment variable for verbose Packer output - Workflow debugging: Use
actor check GitHub Actions run logs for CI/CD issues
References
Official Documentation
Internal Documentation
- CONTRIBUTING.md — Contribution guidelines
- SECURITY.md — Security vulnerability reporting
Tools & Libraries
- Docker Buildx — Multi-platform image builds
- Cosign — Container image signing
- Hashicorp Packer — Marketplace VM image building
- Supervisord — Process management in Bitwarden Lite
- Handlebars — Template engine for config generation
Team Ownership
- Default:
@bitwarden/dept-shot(Self-Host team) - Docker/security files: Also
@bitwarden/team-appsec - Release workflows:
@bitwarden/dept-bre(Release Engineering)