Deploy Vaultwarden on AlmaLinux 10 with Docker and ZeroSSL
Vaultwarden gives you a lightweight self-hosted Bitwarden-compatible password manager that is well suited to small servers, labs, and private infrastructure. It keeps the familiar Bitwarden web vault and client compatibility, while staying easy to run under Docker on a single VPS.
In this guide, we restore a fresh AlmaLinux 10.1 server on Shape.Host, verify the latest stable Vaultwarden release from the official project, install Docker Engine and Docker Compose from Docker’s official EL repository, deploy Vaultwarden 1.35.4, place Nginx in front of it on tutorials.shape.host, secure the site with a trusted ZeroSSL certificate, and validate the finished deployment from both the terminal and a browser.
| Application | Vaultwarden |
|---|---|
| Application version | 1.35.4 |
| Operating system | AlmaLinux 10.1 |
| Container runtime | Docker Engine 29.3.0 with Docker Compose 5.1.1 |
| Reverse proxy | Nginx 1.26.3 |
| Public hostname | tutorials.shape.host |
| TLS issuer | ZeroSSL ECC Domain Secure Site CA |
| Validated on | Live Shape.Host AlmaLinux 10.1 server |
Why Use Vaultwarden on AlmaLinux 10?
- AlmaLinux 10.1 gives you a current enterprise Linux base for a security-sensitive self-hosted service.
- Vaultwarden has a straightforward official Docker Compose deployment path.
- Nginx lets you terminate TLS cleanly while keeping the container bound to localhost.
- ZeroSSL gives you a trusted public certificate for browser and mobile client access.
Before You Begin
Make sure you have the following in place before you start:
- A fresh AlmaLinux 10 server
- Root or sudo access
- A DNS record pointing
tutorials.shape.hostto your server IP - Ports
80and443open to the public internet - Your ZeroSSL EAB key ID and EAB HMAC key for ACME account registration
1. Verify the AlmaLinux 10 Release
Start by confirming that the rebuilt server is actually running AlmaLinux 10.1.
cat /etc/os-release

2. Install Docker, Docker Compose, Nginx, and Base Dependencies
The clean AlmaLinux route is to install Docker Engine from Docker’s official EL repository instead of relying on older distro-packaged container builds. We also install Nginx, Git, firewalld, and the small helper packages needed for certificate automation and SELinux-aware administration.
dnf -y install ca-certificates curl git nginx openssl socat firewalld dnf-plugins-core policycoreutils-python-utils
dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
dnf -y install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
systemctl enable --now docker nginx firewalld
firewall-cmd --permanent --add-service=http
firewall-cmd --permanent --add-service=https
firewall-cmd --reload
docker --version
docker compose version
nginx -v
git --version
firewall-cmd --state
On the validated AlmaLinux 10.1 deployment, this installed Docker Engine 29.3.0, Docker Compose 5.1.1, Nginx 1.26.3, and Git 2.47.3, with firewalld running normally.

3. Create the Vaultwarden Docker Compose Configuration
The official Docker Compose guide shows the latest image tag, but for a stable production-style deployment it is better to pin the current release. The Vaultwarden configuration docs also stress setting DOMAIN to the final public base URL, because several features depend on it being correct.
mkdir -p /opt/vaultwarden
cd /opt/vaultwarden
cat > compose.yaml <<'EOF'
services:
vaultwarden:
image: vaultwarden/server:1.35.4
container_name: vaultwarden
restart: unless-stopped
environment:
DOMAIN: "https://tutorials.shape.host"
SIGNUPS_ALLOWED: "true"
volumes:
- ./vw-data:/data
ports:
- 127.0.0.1:8000:80
EOF
grep '^ image:' compose.yaml
grep '^ DOMAIN:' compose.yaml
grep '^ SIGNUPS_ALLOWED:' compose.yaml
cat compose.yaml
docker compose config --services
The localhost-only bind keeps Vaultwarden off the public interface so Nginx can proxy it securely, while DOMAIN matches the final HTTPS URL recommended by the official configuration overview.

4. Start Vaultwarden
With the compose file in place, start the container and confirm that the web vault answers locally before you expose it through Nginx.
4.1 Launch the Container
cd /opt/vaultwarden
docker compose up -d
sleep 20
On the live server, Docker pulled the official Vaultwarden image, created the data directory, and started the container successfully.

4.2 Validate the Running Container and Local HTTP Response
docker compose ps
docker compose images
curl -I http://127.0.0.1:8000
On the validated deployment, the container settled into a healthy state on image tag 1.35.4 and the local endpoint returned HTTP/1.1 200 OK.

5. Configure Nginx and Issue a ZeroSSL Certificate
The official Vaultwarden Nginx example uses an upstream block plus a websocket-aware map so reverse-proxied keepalive and upgrade requests work correctly. We start with HTTP for the ACME challenge, issue the ZeroSSL certificate, and then switch the site to permanent HTTPS.
mkdir -p /var/www/_letsencrypt /etc/nginx/ssl/tutorials.shape.host
cat > /etc/nginx/conf.d/tutorials.shape.host.conf <<'EOF'
upstream vaultwarden-default {
zone vaultwarden-default 64k;
server 127.0.0.1:8000;
keepalive 2;
}
map $http_upgrade $connection_upgrade {
default upgrade;
'' "";
}
server {
listen 80;
server_name tutorials.shape.host;
location /.well-known/acme-challenge/ {
root /var/www/_letsencrypt;
default_type "text/plain";
}
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;
location / {
proxy_pass http://vaultwarden-default;
}
}
EOF
nginx -t
systemctl reload nginx
curl -fsSL https://get.acme.sh | sh -s email=contact@shape.host
/root/.acme.sh/acme.sh --set-default-ca --server zerossl
/root/.acme.sh/acme.sh --register-account --server zerossl --eab-kid YOUR_ZEROSSL_EAB_KID --eab-hmac-key YOUR_ZEROSSL_EAB_HMAC_KEY
/root/.acme.sh/acme.sh --issue --server zerossl --webroot /var/www/_letsencrypt -d tutorials.shape.host --keylength ec-256
/root/.acme.sh/acme.sh --install-cert -d tutorials.shape.host --ecc \
--fullchain-file /etc/nginx/ssl/tutorials.shape.host/fullchain.cer \
--key-file /etc/nginx/ssl/tutorials.shape.host/tutorials.shape.host.key \
--reloadcmd "systemctl reload nginx"
if command -v getenforce >/dev/null 2>&1 && [ "$(getenforce)" != "Disabled" ]; then
setsebool -P httpd_can_network_connect 1
fi
cat > /etc/nginx/conf.d/tutorials.shape.host.conf <<'EOF'
upstream vaultwarden-default {
zone vaultwarden-default 64k;
server 127.0.0.1:8000;
keepalive 2;
}
map $http_upgrade $connection_upgrade {
default upgrade;
'' "";
}
server {
listen 80;
server_name tutorials.shape.host;
location /.well-known/acme-challenge/ {
root /var/www/_letsencrypt;
default_type "text/plain";
}
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl;
http2 on;
server_name tutorials.shape.host;
ssl_certificate /etc/nginx/ssl/tutorials.shape.host/fullchain.cer;
ssl_certificate_key /etc/nginx/ssl/tutorials.shape.host/tutorials.shape.host.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
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;
location / {
proxy_pass http://vaultwarden-default;
}
}
EOF
nginx -t
systemctl reload nginx
On the validated AlmaLinux 10.1 server, ZeroSSL issued a trusted ECC certificate successfully and Nginx accepted the EL-style TLS syntax of listen 443 ssl; plus http2 on;. On this specific template, SELinux was disabled, so the conditional setsebool block was skipped automatically.

6. Validate the Public HTTPS Deployment
Finish by checking the running container, firewall state, SELinux status, public HTTPS response, and certificate issuer. In the example below, replace 51.89.69.216 with your own public server IP if you are reproducing this on a different machine.
cd /opt/vaultwarden
docker compose ps
docker compose images
firewall-cmd --list-services
sestatus
getsebool httpd_can_network_connect || true
nginx -v
curl -I --resolve tutorials.shape.host:443:51.89.69.216 https://tutorials.shape.host
openssl s_client -connect 51.89.69.216:443 -servername tutorials.shape.host < /dev/null 2>/dev/null | openssl x509 -noout -issuer -subject
On the live deployment, the container stayed healthy, the public site returned HTTP/2 200, and the certificate chain resolved to ZeroSSL ECC Domain Secure Site CA.

7. Open Vaultwarden in Your Browser
Once HTTPS is in place, open https://tutorials.shape.host in a browser. On the first visit, Vaultwarden serves the familiar Bitwarden-compatible login page, and because this tested deployment keeps SIGNUPS_ALLOWED=true for the initial setup, you can create the first account directly from the web interface.

Conclusion
You now have Vaultwarden running on AlmaLinux 10 with Docker Compose, Nginx, and a trusted ZeroSSL certificate on tutorials.shape.host. The live test for this guide confirmed Vaultwarden 1.35.4, Docker Engine 29.3.0, Docker Compose 5.1.1, and Nginx 1.26.3 on a restored Shape.Host AlmaLinux 10.1 server.
After you create the first account, it is a good idea to change SIGNUPS_ALLOWED to false and recreate the container so public registration is no longer exposed.