ADR-0009: Use HashiCorp Packer for Appcircle Server Cloud Marketplace Images¶
- Status: Proposed
- Date: 2026-02-19
- Owners: @osmank, @berk
Context¶
Appcircle Server is offered as a self-hosted option that customers can run on their own infrastructure or via cloud marketplaces (AWS, Azure, Google Cloud). To support marketplace and “launch from image” workflows, we need:
- Pre-built VM images that already have Appcircle Server and its dependencies (e.g. Docker) installed, so instances start quickly and consistently.
- The same environment across clouds: same base OS, same Docker setup, same Appcircle version and install flow, to avoid drift and support issues.
- Repeatable, automated builds so new Appcircle Server versions and new regions can be published without manual, per-cloud steps.
Building and maintaining separate image pipelines per cloud would be costly and error-prone. We need a single, versioned approach that produces images for multiple clouds from one definition.
What Is Packer?¶
Packer is an open-source tool from HashiCorp that automates the creation of machine images. You define:
- Builders – where to run a temporary VM (e.g. AWS, Azure, GCP) and what base image to use.
- Provisioners – what to run on that VM (scripts, package installs, config) before the image is saved.
Packer then launches a VM, runs the provisioners, and creates a final image (AMI, managed image, GCP image, etc.) that you can use to start instances with everything pre-installed.
Key ideas:
- Immutable images – same image id gives the same environment every time.
- Infrastructure as code – image definitions live in HCL/JSON, versioned and reviewable.
- Multi-cloud – one template can target several clouds with the same provisioning logic.
Why We Use Packer¶
- Consistency – Same Ubuntu version, same Docker setup, same Appcircle Server version and steps on every cloud. No drift between “manually set up” vs “scripted” vs “different cloud.”
- Speed – New instances start from a pre-baked image instead of installing Docker and Appcircle from scratch on every boot.
- Repeatability – Builds are reproducible from the repo; we can rebuild images for new regions or new Appcircle versions by re-running Packer.
- Multi-cloud from one place – One repo defines builders for AWS, Azure, and GCP. We can enable or disable clouds by toggling build sources.
Decision¶
We will use HashiCorp Packer to build Appcircle Server machine images for AWS, Azure, and Google Cloud from a single codebase (the server-image-packer repository).
Key points:
- One Packer template defines builders for each cloud (amazon-ebs, azure-arm, googlecompute) and a single provisioner script (
setup-server.sh) that runs on every build. - Base image: Ubuntu 22.04 LTS from each provider’s canonical source (Canonical AMI/offer/image).
- Provisioner installs system dependencies, Docker, and the Appcircle self-hosted server package; runs the install flow and pulls container images; configures swap and any required setup.
- Output: one image per enabled builder (e.g. AMI, Azure managed image, GCP custom image), with version and naming controlled by locals in
build.pkr.hcl. - Version updates: changing
appcircleServerVersion(and related locals) and re-running Packer produces new images; no per-cloud scripting.
Internal documentation for this pipeline lives in the devops section: Appcircle Server Cloud Marketplaces — Image Build.
Options Considered¶
Option A — HashiCorp Packer (Chosen)¶
Pros - Single template and one set of provisioner scripts for all clouds; no duplicated logic. - Immutable, reproducible images; same image id implies same environment. - Infrastructure-as-code: HCL in Git, versioned and reviewable. - Mature builders and community for AWS, Azure, GCP; well-documented. - Easy to add or remove clouds by toggling build sources.
Cons - Team must install and maintain Packer and required plugins. - Build time and cloud API usage when building; acceptable for release cadence.
Option B — Per-cloud scripts (e.g. AWS Image Builder, Azure Image Builder, GCP custom pipelines)¶
Pros - Native to each cloud; some managed automation possible.
Cons - Three (or more) separate pipelines and scripts; high risk of drift and inconsistent behavior. - More operational and maintenance burden; harder to keep versions and steps in sync.
Option C — Terraform (or similar) + cloud-init / user-data only¶
Pros - No separate image-build tool; instances configure themselves at boot.
Cons - Every instance installs Docker and Appcircle from scratch on first boot → slower startup and more failure modes. - Less consistency than a pre-baked image; user-data bugs or network issues affect every launch.
Option D — Manual image creation per cloud¶
Pros - No new tooling.
Cons - Not repeatable; prone to human error and drift; does not scale for version/region matrix.
Consequences¶
Positive¶
- Consistency — Same Ubuntu, Docker, and Appcircle Server setup on every cloud; no drift between “manual” vs “scripted” vs cloud.
- Speed — New instances start from a pre-baked image instead of installing from scratch on every boot.
- Repeatability — Builds are reproducible from the repo; new regions or Appcircle versions are a variable change and a Packer run.
- Single place — One repo (server-image-packer) and one doc (appcircle-cloud-marketplaces) for all cloud image builds.
Follow-ups / migrations¶
- Keep appcircle-cloud-marketplaces.md in sync with server-image-packer (e.g. variables, auth, build commands).