Run an Internal Certificate Authority with step-ca

Khimananda Oli 8 min read Database
Run an Internal Certificate Authority with step-ca

By Khimananda Oli | Last reviewed: August 2026

Managing mutual TLS across dozens of microservices or securing internal APIs often forces teams into complex, proprietary PKI solutions that are difficult to audit and automate. When you need to run an internal certificate authority with step-ca, you gain a lightweight, API-driven alternative to traditional enterprise CAs that integrates natively with modern DevOps workflows. This open-source tool eliminates manual CSR handling while providing the cryptographic rigor required for SOC 2 compliance and zero-trust architectures.

Why should you run an internal certificate authority with step-ca instead of public CAs?

Public CAs like Let's Encrypt solve browser trust perfectly, but they fail for internal infrastructure where privacy, latency, and identity verification matter more than public recognition. Using public certificates for backend service-to-service communication leaks topology information via Certificate Transparency logs and creates unnecessary egress dependencies. When you operate air-gapped environments or handle sensitive data subject to regulations common in Nepal’s fintech sector or global ISO 27001 audits, keeping trust anchors entirely under your control is not optional—it is mandatory.

Traditional enterprise PKI tools like Active Directory Certificate Services or HashiCorp Vault are powerful but heavy. They demand significant operational overhead, Windows licensing, or complex clustering just to issue a simple X.509 certificate. Step-ca occupies a critical middle ground: it is a single static binary written in Go, supports ACME natively, and stores state in flat files or cloud KMS backends. For teams already practicing DevSecOps principles, this means treating certificate issuance as code rather than a ticket-based ritual.

Step-CA ServerRoot + IntermediateACME / JWK / OIDCAPI Service AmTLS Client CertDatabase ProxyShort-lived CertIoT Edge DeviceDevice IdentityTrust Store BundleDistributed to all nodesca.pem + intermediates
Internal PKI architecture: step-ca issues short-lived certificates to services which validate against a distributed trust bundle

The security model here relies on short-lived certificates and automated renewal rather than long-term secrets. In my experience helping Nepali companies achieve compliance, auditors consistently prefer systems where certificate rotation happens automatically every 24 hours over annual manual renewals stored in shared folders. Step-ca makes this default behavior, not an afterthought.

How do you install and initialize step-ca on Ubuntu?

Getting started requires two binaries: step (the CLI) and step-ca (the server). On Ubuntu 24.04 or later, use the official APT repository to ensure you receive security patches automatically. Avoid downloading random binaries from GitHub releases in production; package manager integration ensures proper systemd unit files and user isolation out of the box.

# Install prerequisites and add Smallstep repository
sudo apt update && sudo apt install -y curl gnupg
curl -fsSL https://dl.smallstep.com/keys/apt-key.gpg | sudo gpg --dearmor -o /usr/share/keyrings/smallstep-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/smallstep-archive-keyring.gpg] https://dl.smallstep.com/deb/ubuntu $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/smallstep.list

# Install both CLI and CA server
sudo apt update && sudo apt install -y step-cli step-ca

# Verify installation
step version
step-ca version

Initialization generates your root key pair, intermediate signing key, and default configuration. For any environment beyond local testing, never store the root private key on the same filesystem as the CA server. Use AWS KMS, GCP Cloud KMS, Azure Key Vault, or a hardware security module. The following command creates a standalone CA suitable for development or isolated networks:

# Initialize with sensible defaults for internal use
step ca init \
  --name="Internal Engineering CA" \
  --dns="ca.internal.example.com" \
  --address=":443" \
  --provisioner="[email protected]" \
  --password-file=/tmp/ca-password.txt

# Secure the generated artifacts immediately
chmod 600 $STEPPATH/secrets/root_ca_key
chmod 600 $STEPPATH/secrets/intermediate_ca_key
rm /tmp/ca-password.txt

A common mistake I see in home labs and staging environments is running the CA as root. Create a dedicated step user and configure systemd to bind to port 443 using capabilities or a reverse proxy. If you are managing database connections alongside this PKI, review PostgreSQL administration essentials to understand how to integrate client certificate authentication with your new CA.

Which provisioner type should you choose for automated certificate issuance?

Provisioners define who can request certificates and how they authenticate. Choosing incorrectly leads to either insecure token leakage or operational friction that causes teams to bypass your PKI entirely. Step-ca supports several types, each optimized for different trust boundaries.

  • ACME: Best for servers, Kubernetes ingress controllers, and anything that speaks the ACME protocol. Compatible with cert-manager, Caddy, and Traefik without custom tooling. Supports HTTP-01, DNS-01, and TLS-ALPN-01 challenges.
  • JWK (JSON Web Key): Ideal for CI/CD pipelines and scripts. You embed a signed JWT in your automation, and the CA validates it cryptographically. Tokens can be scoped to specific SANs and durations.
  • OIDC: Perfect for developer laptops and human operators. Integrates with Google Workspace, Okta, or Keycloak so engineers get certificates based on their corporate identity without managing keys.
  • X5C: Used when you already have certificates and want to issue derivatives. Common in IoT device provisioning chains where a manufacturing cert signs initial enrollment requests.
ACME ProvisionerChallenge-based validationcert-manager / Caddy / nginxJWK ProvisionerSigned JWT token authCI/CD pipelines / scriptsOIDC ProvisionerIdentity provider SSODeveloper workstationsStep-CA CorePolicy EngineCertificate SignerAudit LoggerServer Cert24h TTLPipeline CertScoped SANsUser CertEmail-bound
Provisioner selection matrix: ACME for infrastructure, JWK for automation, OIDC for humans—all feeding the same policy engine

For most teams starting fresh, enable ACME first. It gives you immediate compatibility with the broader ecosystem. Add JWK provisioners specifically for your CI runners—never share the same provisioner between interactive users and automated systems. This separation simplifies revocation and audit trails significantly.

How do you automate certificate renewal in CI/CD and Kubernetes?

The entire value proposition of running an internal certificate authority with step-ca disappears if engineers still manually copy PEM files. Automation must be the only path to obtaining certificates. In Kubernetes, deploy cert-manager with the step-ca ACME issuer. This handles pod restarts, secret updates, and renewal windows transparently.

# Example cert-manager ClusterIssuer for step-ca ACME
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: internal-step-ca
spec:
  acme:
    server: https://ca.internal.example.com/acme/acme/directory
    privateKeySecretRef:
      name: step-ca-acme-account-key
    solvers:
    - http01:
        ingress:
          class: nginx

For non-Kubernetes workloads like bare-metal servers or VMs, use the step ca renew daemon mode. It runs as a systemd service, monitors certificate expiry, and renews automatically using the embedded renewal token. This is far superior to cron jobs because it handles jitter, retries with backoff, and reloads dependent services via hooks.

# Enable automatic renewal daemon for a web server cert
sudo systemctl enable --now [email protected]

# Or manually trigger renewal with daemon mode
step ca renew --daemon --renew-period 12h \
  /etc/ssl/private/server.crt \
  /etc/ssl/private/server.key \
  --exec "systemctl reload nginx"

In CI/CD pipelines, generate ephemeral tokens at runtime rather than storing long-lived credentials. With GitHub Actions or GitLab CI, inject a pre-signed JWT as a job variable. The pipeline requests a certificate valid only for the build duration, uses it to sign artifacts or connect to staging databases, and discards it. This aligns with secure secret handling practices and prevents credential sprawl across runner caches.

How does step-ca compare to Vault and Let's Encrypt for internal PKI?

Choosing the right tool depends entirely on your operational constraints, team size, and compliance requirements. There is no universal best option—only trade-offs that match your specific context.

CriteriaStep-CAHashiCorp VaultLet's Encrypt
Deployment ComplexitySingle binary, minimal configMulti-component cluster, Raft consensusSaaS, zero infra
Internal/Private IPsNative supportNative supportNot supported
ACME SupportBuilt-in, first-classVia plugin/configurationPrimary interface
KMS IntegrationAWS/GCP/Azure/YubiKeyExtensive HSM/KMS supportN/A (managed)
Operational OverheadLow (hours to production)High (weeks to harden properly)None
Certificate TransparencyOptional, configurableOptional, configurableMandatory public logging
Best ForSMBs, microservices, edgeLarge enterprises, multi-tenantPublic-facing websites
Need Internal Certs?Team Size < 10 Engineers?YesNoUse Step-CALow ops burdenACME nativeEvaluate VaultMulti-tenancy neededComplex policiesPublic Website Only?→ Use Let's Encrypt
PKI decision framework: team size and tenancy requirements drive the choice between step-ca and enterprise alternatives

If your organization has fewer than ten platform engineers and does not require dynamic secret generation or multi-tenant isolation, Vault introduces complexity that rarely pays off. Step-ca delivers 90% of the security benefits with 10% of the operational cost. Reserve Vault for environments where multiple business units share infrastructure and need strict namespace isolation with separate encryption domains.

Secure Your Internal PKI Foundation Today

When you run an internal certificate authority with step-ca, you are building foundational infrastructure that every other security control depends upon. Start with ACME provisioners for your highest-value services, enforce short-lived certificates universally, and automate renewal before humans ever touch a PEM file. Monitor issuance metrics through Prometheus endpoints and treat CA availability as a tier-zero dependency. If your team needs guidance designing a compliant PKI architecture or integrating step-ca with existing observability stacks, reach out to discuss your specific requirements. Proper certificate management prevents more incidents than almost any other single investment in your security posture.

Frequently Asked Questions

Step-ca is a lightweight, open-source certificate authority written in Go. It automates internal TLS issuance for microservices and devices without relying on public CAs or complex PKI infrastructure like HashiCorp Vault.

Download the latest .deb package from GitHub releases and install via dpkg. Initialize with step ca init to generate root keys, configure defaults.json, and start the systemd service immediately after installation completes successfully.

Yes, step-ca natively supports ACME v2. Configure an ACME provisioner in your CA config, then point certbot or lego to your internal ACME endpoint using the --server flag for automatic certificate issuance and renewal.

Yes.

Step-ca is simpler to deploy and maintain for pure certificate management. Vault offers broader secrets management but requires more resources and operational overhead if you only need automated internal TLS certificate issuance and renewal capabilities.

Store the root key offline or in a hardware security module. For intermediate signing keys, use cloud KMS like AWS KMS or GCP Cloud KMS. Never keep unencrypted private keys on the same filesystem as the CA binary.

Generate a new intermediate CSR signed by your offline root. Update step-ca configuration to include both old and new intermediates during transition. Revoke the old intermediate only after all active certificates expire or are reissued through automated renewal workflows.

Absolutely.

Use step ca renew --daemon in a systemd service unit. Configure Restart=always and set renewal windows in defaults.json. The daemon handles retries automatically and updates certificate files before expiry without requiring cron jobs or external orchestration tools.

Step-ca supports JWK, OIDC, ACME, SSHPOP, and cloud provider metadata provisioners. Choose JWK for service accounts, OIDC for human users, and cloud metadata for automatic VM authentication based on instance identity documents and IAM roles.

Verify the client trusts your root CA bundle. Check certificate validity dates and SANs match the requested hostname. Inspect step-ca logs with journalctl and validate chain completeness using openssl s_client against your internal CA endpoint directly.

Yes, step-ca issues SSH certificates alongside X.509. Configure SSHPOP or OIDC provisioners for user authentication and enable ssh section in ca.json. This enables passwordless SSH access with short-lived credentials tied to your existing identity provider.

Step-ca supports BadgerDB, MySQL, PostgreSQL, and SQLite for certificate storage and revocation tracking. Use PostgreSQL or MySQL for multi-instance deployments requiring shared state. BadgerDB works well for single-node setups with lower operational complexity and no external dependencies.

Deploy step-ca as a StatefulSet with persistent storage. Expose via ClusterIP Service and use cert-manager with step-ca issuer for pod certificate automation. Restrict network policies to allow only authorized namespaces and enforce mTLS between services consuming internal certificates.

Existing certificates remain valid until expiration. New issuance and renewals fail until recovery. Configure high availability with multiple replicas sharing a database backend. Implement health checks and alerting to detect outages before they impact certificate lifecycle operations across services.