Install#
There are a few methods for produciton installation. The install script is recommended for most deployments due to it’s simplicity.
| Method | Best for |
|---|---|
| Install script | Standard Linux servers, systemd environments |
| Build from source | Development, or architectures not covered by releases |
| Docker | Standalone container-based deployments |
Install Script (Recommended)#
The install script downloads the appropriate release binary, creates a dedicated service user, installs a ca.json template, and registers a hardened systemd service unit.
curl -fsSL https://raw.githubusercontent.com/esnet/acme-proxy/main/install.sh | sudo shEnvironment variable overrides#
All defaults are overridable:
# Defaults
INSTALL_DIR=/opt/acme-proxy
DB_DIR=/opt/acme-proxy/db
CONFIG_FILE=/opt/acme-proxy/ca.json
SERVICE_USER=acme-proxy
SERVICE_GROUP=acme-proxyExample — custom paths and user:
curl -fsSL https://raw.githubusercontent.com/esnet/acme-proxy/main/install.sh | \
sudo INSTALL_DIR=/usr/local/acme-proxy SERVICE_USER=acmeservice shWhat the script installs#
| Path | Description |
|---|---|
$INSTALL_DIR/step-ca | The server binary |
$INSTALL_DIR/ca.json | Configuration file (template — must be edited) |
$DB_DIR/bbolt | bbolt KV store for ACME account state |
/etc/systemd/system/acme-proxy.service | Systemd service unit |
The service is enabled but not started. Configure ca.json file before starting.
Build from Source#
Requirements: Go >= 1.25, libpcsclite-dev (Debian/Ubuntu) or pcsc-lite-devel (RHEL/Rocky)
# Install build dependency
sudo apt-get install -y libpcsclite-dev pkg-config # Debian / Ubuntu
sudo dnf install -y pcsc-lite-devel pkgconfig # RHEL / Rocky
# Clone and build
git clone https://github.com/esnet/acme-proxy.git
cd acme-proxy
makeThe build produces a step-ca binary in the current directory. Copy it to your install location:
# configure `ca.json` file before starting step-ca
./step-ca ca.jsonUse the installer script as a reference to complete the setup with systemd service unit, service account user, permissions etc.
Install using Docker#
Run the following commands before starting the container on a linux host
mkdir -p /opt/acme-proxy/db
touch /opt/acme-proxy/ca.json
chown -R 65532:65532 /opt/acme-proxyMake sure you have configured the ca.json file
docker run -d \
--name acme-proxy \
-p 443:443 \
-v "$(pwd)"/ca.json:/opt/acme-proxy/ca.json:ro \
-v "$(pwd)"/db:/opt/acme-proxy/db \
--restart unless-stopped \
ghcr.io/esnet/acme-proxy:latestView logs:
docker logs -f acme-proxyDocker Compose#
services:
acme-proxy:
image: ghcr.io/esnet/acme-proxy:latest
ports:
- "443:443"
volumes:
- ./ca.json:/opt/acme-proxy/ca.json:ro
- ./db:/opt/acme-proxy/db
restart: unless-stoppedStarting the Service#
Systemd#
sudo systemctl start acme-proxy
sudo systemctl status acme-proxyOn first start, acme-proxy registers an account with the upstream CA and obtains a TLS certificate for itself. This takes a few seconds. Follow the logs:
sudo journalctl -u acme-proxy -fExpected startup sequence:
Building new tls configuration using step-ca x509 Signer Interface
Initializing ACME client...
[INFO] acme: Registering account for certadmin@example.com
[INFO] [acmeproxy.example.com] acme: Obtaining bundled SAN certificate
[INFO] [acmeproxy.example.com] acme: Validations succeeded; requesting certificates
Successfully obtained certificate from external CA
Serving HTTPS on :443 ...Running manually (without systemd)#
/opt/acme-proxy/step-ca /opt/acme-proxy/ca.jsonDocker#
docker compose up -d
docker logs -f acme-proxyVerify#
curl -s https://acmeproxy.example.com/acme/acme/directory | jq .
{
"newNonce": "https://proxy.example.com/acme/acme/new-nonce",
"newAccount": "https://proxy.example.com/acme/acme/new-account",
"newOrder": "https://proxy.example.com/acme/acme/new-order",
"revokeCert": "https://proxy.example.com/acme/acme/revoke-cert",
"keyChange": "https://proxy.example.com/acme/acme/key-change"
}A JSON object with newNonce, newAccount, newOrder keys confirms the ACME server is running and accepting requests.
Verifying Release Binaries#
Each release binary is accompanied by a SHA256 checksum file and a cosign signature bundle for supply-chain verification.
Checksum#
VERSION=v1.0.0 # replace with the release version
curl -fsSLO "https://github.com/esnet/acme-proxy/releases/download/${VERSION}/step-ca_linux_amd64"
curl -fsSLO "https://github.com/esnet/acme-proxy/releases/download/${VERSION}/SHA256SUMS"
sha256sum --check --ignore-missing SHA256SUMSSignature#
Binaries are signed using keyless signing via GitHub Actions OIDC — no long-lived signing key exists. Verification requires the .bundle file published alongside each binary.
# Install cosign: https://docs.sigstore.dev/cosign/system_config/installation/
VERSION=v1.0.0 # replace with the release version
curl -fsSLO "https://github.com/esnet/acme-proxy/releases/download/${VERSION}/step-ca_linux_amd64"
curl -fsSLO "https://github.com/esnet/acme-proxy/releases/download/${VERSION}/step-ca_linux_amd64.bundle"
cosign verify-blob \
--bundle step-ca_linux_amd64.bundle \
--certificate-identity "https://github.com/esnet/acme-proxy/.github/workflows/ci.yml@refs/tags/${VERSION}" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
step-ca_linux_amd64A successful verification prints:
Verified OKSubstitute amd64 with arm64 for the ARM binary.