Installation
One-Line Installer (Recommended)
The interactive installer works on Linux and macOS. It installs Docker if missing, starts the daemon, generates every secret, writes .env, applies database migrations, and brings the stack up.
curl -fsSL https://vaultctl.vinelabs.de/install.sh | bashWhat it does:
- Checks for Docker and the Compose plugin (installs Docker if missing, starts the daemon)
- Downloads the vaultctl source into your chosen install directory
- Asks how to deploy:
- Full stack — Caddy auto-TLS + vaultctl + Postgres (prompts for a public domain)
- Simple — vaultctl + Postgres bound to
127.0.0.1:PORT(prompts for the port; front it with your own reverse proxy)
- Generates cryptographic secrets (JWT, peppers, data-encryption key) and writes
.env - Pulls the vaultctl + PostgreSQL images and starts all services
- Applies database migrations automatically
- Waits for the health check and prints your vault URL
The installer is fully interactive — it reads every prompt from your terminal, so it works even when piped through curl ... | bash. Each prompt has a sensible default.
macOS: the installer uses Homebrew (opens in a new tab) to install a headless Docker runtime (colima (opens in a new tab) + the docker CLI) when Docker is not already present, then runs colima start for you. Install Homebrew first if you don't have it. Docker Desktop also works if it's already installed.
Unattended installs: export any of these before running to skip the matching prompt — VAULTCTL_INSTALL_DIR, VAULTCTL_MODE (1 full / 2 simple), VAULTCTL_DOMAIN, VAULTCTL_HOST_PORT, VAULTCTL_AUTO_INSTALL_DOCKER=y, VAULTCTL_AUTO_START=y, VAULTCTL_OVERWRITE_ENV=y.
Docker Images
Official multi-arch images (linux/amd64, linux/arm64) are published to both registries on every release. All images are signed with cosign (opens in a new tab) keyless signatures.
docker pull vineethnkrishnan/vaultctl:latest
# or pin to a specific version
docker pull vineethnkrishnan/vaultctl:1.1.3Verify image signature
cosign verify vineethnkrishnan/vaultctl:latest \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
--certificate-identity-regexp 'github\.com/vineethkrishnan/vaultctl'Run directly
docker run -d --name vaultctl \
-p 8080:8080 \
-e VAULTCTL_DATABASE_URL="postgres://user:pass@host:5432/vaultctl?sslmode=disable" \
-e VAULTCTL_JWT_SECRET_CURRENT="$(openssl rand -base64 32)" \
-e VAULTCTL_SERVER_PEPPER="$(openssl rand -base64 32)" \
-e VAULTCTL_ENUMERATION_PEPPER="$(openssl rand -base64 32)" \
-e VAULTCTL_DATA_ENCRYPTION_KEY="$(openssl rand -base64 32)" \
vineethnkrishnan/vaultctl:latestAlways put a TLS-terminating reverse proxy (Caddy, nginx, Traefik) in front. Never expose port 8080 directly to the internet.
Docker Compose
Two compose variants are provided in the repository.
Full Stack — Caddy + vaultctl + PostgreSQL
Includes Caddy for automatic HTTPS via Let's Encrypt.
Clone the repository
git clone https://github.com/vineethkrishnan/vaultctl.git
cd vaultctlConfigure environment
cp .env.example .envEdit .env and set these required values:
VAULTCTL_ENV=production
VAULTCTL_BASE_URL=https://vault.yourdomain.com
VAULTCTL_DB_PASSWORD=<generate-a-strong-password>
# Generate each with: openssl rand -base64 32
VAULTCTL_JWT_SECRET_CURRENT=<generate>
VAULTCTL_SERVER_PEPPER=<generate>
VAULTCTL_ENUMERATION_PEPPER=<generate>
VAULTCTL_DATA_ENCRYPTION_KEY=<generate>Generate secrets
for var in JWT_SECRET_CURRENT SERVER_PEPPER ENUMERATION_PEPPER DATA_ENCRYPTION_KEY; do
echo "VAULTCTL_${var}=$(openssl rand -base64 32)"
done >> .envStart services
docker compose up -dApply database migrations
The server does not migrate on its own. Run this once after the database is healthy (re-run safely after every upgrade):
docker compose exec -T vaultctl /usr/local/bin/vaultctl migrate upVerify
curl https://vault.yourdomain.com/api/v1/health
# → {"status":"ok"}Binary
Pre-built binaries are available on GitHub Releases (opens in a new tab) for all major platforms. Each release includes SHA-256 checksums signed with cosign and per-archive SBOMs.
The quickest binary install is the installer's --no-docker mode — it picks the right asset, verifies the checksum, generates secrets, writes config.env, and offers to run migrations:
curl -fsSL https://vaultctl.vinelabs.de/install.sh | bash -s -- --no-dockerYou provide your own PostgreSQL and reverse proxy. The manual steps below do the same thing by hand.
Release asset names include the version (e.g. vaultctl_1.4.3_linux_amd64.tar.gz), so resolve the latest tag first:
VERSION=$(curl -fsSL https://api.github.com/repos/vineethkrishnan/vaultctl/releases/latest | grep -m1 '"tag_name"' | cut -d'"' -f4)
# amd64 (use _arm64 on ARM hosts)
curl -L "https://github.com/vineethkrishnan/vaultctl/releases/download/${VERSION}/vaultctl_${VERSION#v}_linux_amd64.tar.gz" | tar xz
sudo mv vaultctl /usr/local/bin/Verify checksums
# Download checksums and signature
curl -LO https://github.com/vineethkrishnan/vaultctl/releases/latest/download/checksums.txt
curl -LO https://github.com/vineethkrishnan/vaultctl/releases/latest/download/checksums.txt.sig
curl -LO https://github.com/vineethkrishnan/vaultctl/releases/latest/download/checksums.txt.pem
# Verify signature
cosign verify-blob checksums.txt \
--signature checksums.txt.sig \
--certificate checksums.txt.pem \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
--certificate-identity-regexp 'github\.com/vineethkrishnan/vaultctl'
# Verify file integrity
sha256sum -c checksums.txt --ignore-missingPrerequisites
- PostgreSQL 16+ running and accessible
- A reverse proxy (Caddy or nginx) for TLS termination
Run
Configuration is read from environment variables (see Configuration); there is no --config flag. Put your settings in an env file and load it before running:
# Load config (VAULTCTL_DB_*, secrets, VAULTCTL_BASE_URL, ...) into the environment
set -a; . /etc/vaultctl/config.env; set +a
# Run database migrations (first run, and after every upgrade)
vaultctl migrate up
# Start the server (listens on VAULTCTL_PORT, default :8080)
vaultctl serverKubernetes
A Helm chart is planned for a future release. For now, use the Docker image directly:
apiVersion: apps/v1
kind: Deployment
metadata:
name: vaultctl
spec:
replicas: 1
selector:
matchLabels:
app: vaultctl
template:
metadata:
labels:
app: vaultctl
spec:
containers:
- name: vaultctl
image: ghcr.io/vineethkrishnan/vaultctl:latest
ports:
- containerPort: 8080
envFrom:
- secretRef:
name: vaultctl-secrets
livenessProbe:
httpGet:
path: /api/v1/health
port: 8080
initialDelaySeconds: 5
readinessProbe:
httpGet:
path: /api/v1/health
port: 8080
initialDelaySeconds: 3
---
apiVersion: v1
kind: Service
metadata:
name: vaultctl
spec:
selector:
app: vaultctl
ports:
- port: 80
targetPort: 8080Create a Secret with all required environment variables. See the Configuration page for the complete list.
You can use either ghcr.io/vineethkrishnan/vaultctl or vineethnkrishnan/vaultctl (Docker Hub) — both are identical multi-arch images.
Post-Installation
After installation, open your browser and navigate to your server URL.
- Click Create Account
- Follow the Your First Account guide
- Install the Browser Extension
Security reminder: Each cryptographic secret (JWT_SECRET, SERVER_PEPPER, ENUMERATION_PEPPER, DATA_ENCRYPTION_KEY) must be unique. Never reuse secrets across deployments. Store them separately from database backups.