Skip to content

Blog

Installing Vaultwarden via Docker

1. Install Docker

Install Docker on your system first.

2. Pull Docker Images

  • vaultwarden/server:1.33.2
  • openresty/openresty:bookworm
mkdir -p /home/vaultwarden /home/openresty/{ssl,conf.d}

3. Vaultwarden Setup

Create a dedicated Vaultwarden network to avoid direct exposure

docker network create --driver bridge vaultwarden_network

Create the Vaultwarden startup file at /home/vaultwarden/compose.yml

services:
  vaultwarden:
    image: vaultwarden/server:1.33.2
    container_name: vaultwarden
    restart: unless-stopped
    environment:
      TZ: Asia/Shanghai
      ENABLE_WEBSOCKET: false
      # SIGNUPS_ALLOWED: false # Disable new user registration
      SIGNUPS_ALLOWED: true # Allow new user registration
      # ADMIN_TOKEN: Argon2 PHC hashed password for /admin access
      # Generate using: echo -n "YourPassword" | argon2 "$(openssl rand -base64 32)" -e -id -k 65540 -t 3 -p 4
      # Or use: docker run --rm -it vaultwarden/server /vaultwarden hash
      ADMIN_TOKEN: xxx
    volumes:
      - ./vw-data:/data
    networks:
      - vaultwarden_network
networks:
  vaultwarden_network:
    external: true

Start Vaultwarden

cd /home/vaultwarden
docker compose up -d

4. Generate an SSL Certificate

cd /home/openresty/ssl
# Generate private key
openssl ecparam -name prime256v1 -genkey -noout -out key.pem
# Create certificate signing request
openssl req -new -key key.pem -out server.csr \
    -subj "/CN=localhost"
# Generate self-signed certificate
openssl x509 -req -in server.csr -signkey key.pem -out cert.pem -days 3650

5. OpenResty Setup

Configure /home/openresty/nginx.conf

pcre_jit on;
worker_processes auto;
events {
    worker_connections 1024;
}
http {
    log_format main '$remote_addr - $remote_user [$time_iso8601] "$request" '
    '$status $body_bytes_sent "$http_referer" '
    '"$http_user_agent" "$http_x_forwarded_for"'
    '"$request_time" "$upstream_response_time"';
    open_log_file_cache max=1024 inactive=1m valid=5m min_uses=2;
    access_log /var/log/nginx/access.log main;
    error_log /var/log/nginx/error.log debug;
    underscores_in_headers off;
    include mime.types;
    default_type application/octet-stream;
    client_body_temp_path /var/run/openresty/nginx-client-body;
    proxy_temp_path /var/run/openresty/nginx-proxy;
    fastcgi_temp_path /var/run/openresty/nginx-fastcgi;
    uwsgi_temp_path /var/run/openresty/nginx-uwsgi;
    scgi_temp_path /var/run/openresty/nginx-scgi;
    sendfile on;
    keepalive_timeout 65;
    server_tokens off;
    resolver 127.0.0.11 valid=30s;
    types_hash_max_size 4096;
    client_max_body_size 1024M;
    client_body_timeout 1m;
    proxy_connect_timeout 1m;
    proxy_read_timeout 10m;
    proxy_send_timeout 10m;
    include /etc/nginx/conf.d/*.conf;
}

Configure /home/openresty/conf.d/vaultwarden.conf

upstream vaultwarden-default {
    zone vaultwarden-default 64k;
    server vaultwarden:80;
    keepalive 2;
}
map $http_upgrade $connection_upgrade {
    default upgrade;
    '' "";
}
server {
    listen 80;
    return 301 https://$host$request_uri;
}
server {
    listen 443 ssl;
    http2 on;
    server_name _;
    ssl_certificate /etc/nginx/ssl/cert.pem;
    ssl_certificate_key /etc/nginx/ssl/key.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers off;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 1h;
    client_max_body_size 525M;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_connect_timeout 777;
    proxy_send_timeout 777;
    proxy_read_timeout 777;
    send_timeout 777;
    location / {
      proxy_pass http://vaultwarden-default;
    }
}

Configure /home/openresty/compose.yml

services:
  openresty:
    image: openresty/openresty:bookworm
    container_name: openresty
    restart: unless-stopped
    environment:
      - TZ=Asia/Shanghai
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/usr/local/openresty/nginx/conf/nginx.conf:ro
      - ./conf.d:/etc/nginx/conf.d/:ro
      - ./default.d/:/etc/nginx/default.d/:ro
      - ./log:/var/log/nginx/:rw
      - ./ssl:/etc/nginx/ssl/:ro
      - ./socks:/var/run/socks
    networks:
      - vaultwarden_network
networks:
  vaultwarden_network:
    external: true
    driver: bridge

Start OpenResty

cd /home/openresty
docker compose up -d

6. Access Vaultwarden

7. Toggle New User Registration

To enable/disable new user registration: 1. Modify /home/vaultwarden/compose.yml: toggle the SIGNUPS_ALLOWED value 2. Restart Vaultwarden: docker compose restart

Users can be managed through the admin interface.

8. Browser Setup

Install the Bitwarden browser extension to start using Vaultwarden.

Deploying a CI/CD Infrastructure with Docker Compose

In this article, I will demonstrate how to deploy a CI/CD infrastructure using Docker Compose. It will require the following components:

  • a database (postgres)
  • a git server (gitea)
  • a reverse proxy server (openresty)
  • jekins(not finished)

1. Create Two Docker Network Bridges

# For any app that needs to connect to PostgreSQL
docker network create --driver bridge postgres_network
# For the reverse proxy server to connect to the Gitea web service
docker network create --driver bridge gitea_network

2. Start Postgres

Use the Docker Compose file to start a PostgreSQL server, Besides the offical postgres image, Bitnami/PostgreSQL is also a good choice as it provides more flexable control over postgres.

3. Config Gitea Database

# Replace 'giteaPassword' with a strong password
docker compose exec -it postgres psql -U postgres -c "CREATE ROLE gitea WITH LOGIN PASSWORD 'giteaPassword';"
docker compose exec -it postgres psql -U postgres -c "CREATE DATABASE giteadb WITH OWNER gitea TEMPLATE template0 ENCODING UTF8 LC_COLLATE 'en_US.UTF-8' LC_CTYPE 'en_US.UTF-8';"

4. Start Gitea

Use the Docker Compose file to start up gitea server. Remember to replace GITEA__database__PASSWD and SECRET_KEY with strong values., the HTTP_PORT is irrelevant as we will only expose it on the internal gitea_network. To publish the Gitea service with a subpath like /gitea, configure DOMAIN and ROOT_URL accordingly.

After Gitea starts, create an administrator account

docker compose exec -it gitea su git -c "gitea admin user create --username <ADMIN> --password <AdminPassword> --email <AdminEmailAddress>"

5. Reverse Proxy Server

I'll use OpenResty as a reverse proxy server. It's like an Nginx server with many plugins, so I won't need to recompile Nginx for additional functionality.

First, create an Nginx configuration file. Similar to the official Nginx Docker image, the OpenResty Docker image allows users to customize the configuration file by overwriting /etc/nginx/conf.d/default.conf within the container. The main configuration file /usr/local/openresty/nginx/conf/nginx.conf has an entry include conf.d/*.conf. Here's my configuration file:

# The default Docker DNS resolver, required for Nginx to resolve Gitea container IP
resolver 127.0.0.11 valid=30s;
access_log /var/log/nginx/access.log ;
error_log /var/log/nginx/error.log ;
server {
   listen 443;
   server_name _;
   charset utf-8;
   # gitea service will also need a 'v2' sub path
   location ~ ^/(gitea|v2)($|/) {
      client_max_body_size 512M;
      rewrite ^ $request_uri;
      rewrite ^(/gitea)?(/.*) $2 break;
      proxy_pass http://gitea:3000$uri;
      proxy_set_header Connection $http_connection;
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Host $host;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header X-Forwarded-Proto $scheme;
   }
}

Then, start the OpenResty server using the Compose file. After that, we can access the gitea server using the accout we created

6. Jenkins

...