Kubernetes (Helm)
The provided Helm chart runs as2d on any conformant cluster — AKS, EKS, GKE, k3s, bare metal. It has been verified end-to-end on a live cluster in both its storage profiles.
Install
# Your key material (aliases resolve as {alias}.key.pem / {alias}.cer.pem):
kubectl create secret generic as2d-keys \
--from-file=me.key.pem --from-file=me.cer.pem --from-file=partner.cer.pem
# Your license (key name must be license.lic):
kubectl create secret generic as2d-license --from-file=license.lic
helm install as2d ./as2d \
--set-file partnerships=partnerships.toml \
--set licenseSecret=as2d-license \
--set image.repository=<registry>/as2d --set image.tag=<tag>
The chart refuses to install without a license Secret unless you
explicitly pass --set allowUnlicensed=true (which deploys in a degraded
state — health answers, AS2 traffic 503s — useful for platform smoke
tests before purchase completes).
Storage profiles
| Profile | Values | External services |
|---|---|---|
| Single replica (default) | storage.kind=local | none — SQLite + a PVC |
| Scale-out, self-hosted | storage.kind=postgres postgres.internal=true replicaCount=N | an in-cluster Postgres the chart deploys for you |
| Scale-out, managed DB | storage.kind=postgres postgres.url=... | any managed PostgreSQL |
The chart enforces the safety rule for you: more than one replica requires the Postgres backend (local SQLite state is single-replica by design), with payloads stored in the database so every replica sees them.
Azure-native storage on AKS
Payloads in Blob Storage and/or private keys in Key Vault compose with
either profile via extraConfig:
extraConfig: |
[storage.blob]
account = "myaccount"
account_key = "..."
container = "as2-payloads"
endpoint = "https://myaccount.blob.core.windows.net"
[storage.key_vault]
vault_url = "https://myvault.vault.azure.net"
Operational notes
- License renewal without restart: update the Secret in place —
Kubernetes updates the mounted file and as2d re-reads it within the hour. A corrupted update is harmless: the server keeps the last good license and logs the problem.kubectl create secret generic as2d-license --from-file=license.lic \ --dry-run=client -o yaml | kubectl apply -f - - Readiness stays 200 even when the license degrades — deliberately.
An expired deployment should tell partners “503, retry later” at the
protocol level, not vanish from the load balancer (which partners see
as an outage with no diagnosis). Monitor the
as2_license_stategauge or the/healthzbody instead of tightening the probe. - Metrics:
--set metrics.enabled=trueopens the dedicated metrics port with Prometheus scrape annotations; it is never exposed on the partner-facing Service. - TLS terminates at your ingress controller, per the container-first design.
- With
postgres.internal=truethe as2d pods may restart a few times on first install while Postgres initializes — connecting at boot is deliberate (a bad database URL should fail loudly, not at the first message). They settle once Postgres is ready.