Installation

Prerequisites, dependency installation, and first run

Tip

Prefer Docker? Skip the manual setup — see Docker for a single-command deployment with docker compose up -d.

Prerequisites

Requirement

Version

Notes

Python

3.12+

Required for modern type syntax

PostgreSQL

14+

Primary data store

pip

Latest

Package installer

Git

Any

For cloning the repo

Note

Optional: HSM Support

If you plan to use the HSM (PKCS#11) CA backend, you also need the python-pkcs11 package and a compatible PKCS#11 library installed on your system.

Clone the Repository

git clone https://github.com/miichoow/ACMEEH.git
cd acmeeh

Create a Virtual Environment

# Linux / macOS
python3.12 -m venv .venv
source .venv/bin/activate

# Windows
python -m venv .venv
.venv\Scripts\activate

Install Dependencies

# Core dependencies
pip install flask cryptography dnspython jinja2 "psycopg[binary]"

# ConfigKit (configuration management) & PyPGKit (PostgreSQL ORM layer)
pip install pyConfigKit PyPGKit

# Optional: HSM support (PKCS#11 backends)
pip install python-pkcs11

# Optional: ACME proxy CA backend
pip install acmeow

Set Up PostgreSQL

Create a database and user for ACMEEH:

# Connect to PostgreSQL as superuser
psql -U postgres

# Create database and user
CREATE USER acmeeh WITH PASSWORD 'your_secure_password';
CREATE DATABASE acmeeh OWNER acmeeh;
\q

Tip

Auto-Setup

Set database.auto_setup: true in your config and ACMEEH will automatically create all tables and triggers on first startup. The SQL schema uses IF NOT EXISTS so it’s safe to run repeatedly.

Create a Configuration File

Create a config.yaml with at minimum the required fields:

server:
  external_url: https://acme.example.com

database:
  host: localhost
  port: 5432
  database: acmeeh
  user: acmeeh
  password: ${DB_PASSWORD}
  auto_setup: true

ca:
  backend: internal
  internal:
    root_cert_path: /path/to/root-ca.pem
    root_key_path: /path/to/root-ca-key.pem

challenges:
  enabled:
    - http-01

See the Configuration Reference for all available settings.

Generate a Test CA

For development and testing, generate a self-signed root CA:

# Generate a root CA key and certificate using openssl
openssl ecparam -genkey -name prime256v1 -out root-ca-key.pem
openssl req -new -x509 -key root-ca-key.pem -out root-ca.pem \
    -days 3650 -subj "/CN=ACMEEH Development CA"

Validate Configuration

PYTHONPATH=src python -m acmeeh -c config.yaml --validate-only

This loads and validates the config file against the JSON Schema, resolves environment variables, builds the typed settings tree, and exits. If valid, it prints a summary:

  server.external_url   = https://acme.example.com
  server.bind           = 0.0.0.0:8443
  server.workers        = 4
  database              = acmeeh@localhost:5432/acmeeh
  ca.backend            = internal
  challenges.enabled    = ['http-01']
  logging.level         = INFO
  tos.require_agreement = False
  admin_api.enabled     = False
Configuration valid: config.yaml

Start the Server

Development Mode

# Flask development server with auto-reload
PYTHONPATH=src DB_PASSWORD=secret python -m acmeeh -c config.yaml --dev

Production Mode

# Gunicorn production server
PYTHONPATH=src DB_PASSWORD=secret python -m acmeeh -c config.yaml

See the Deployment for production setup recommendations.

Verify It Works

# Fetch the ACME directory
curl -sk https://localhost:8443/directory | python -m json.tool

You should see a JSON response with the ACME directory URLs:

{
    "newNonce": "https://acme.example.com/new-nonce",
    "newAccount": "https://acme.example.com/new-account",
    "newOrder": "https://acme.example.com/new-order",
    "newAuthz": "https://acme.example.com/new-authz",
    "revokeCert": "https://acme.example.com/revoke-cert",
    "keyChange": "https://acme.example.com/key-change"
}

Test CA Signing

# Verify the CA backend can sign certificates
PYTHONPATH=src DB_PASSWORD=secret python -m acmeeh -c config.yaml ca test-sign