carlos / platform Public

docs: PoC runs the Buildkite amd64 images unmodified on x86 boxes

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Paul Campbell pushed by paul@keymail.dev 69b4a7dbb1c0134cc2b39830aa34098219860a89 parent 8d0e410
1 file changed, +28 −19
  • docs/superpowers/specs/2026-08-11-rails-behind-carlos-edge-poc-design.md +28 −19
diff --git a/docs/superpowers/specs/2026-08-11-rails-behind-carlos-edge-poc-design.md b/docs/superpowers/specs/2026-08-11-rails-behind-carlos-edge-poc-design.md
index 7e74d81ef..d52aa0091 100644
--- a/docs/superpowers/specs/2026-08-11-rails-behind-carlos-edge-poc-design.md
+++ b/docs/superpowers/specs/2026-08-11-rails-behind-carlos-edge-poc-design.md
@@ -12,11 +12,13 @@ deployment is a real option rather than a slogan.
Tito Classic and Tito Pro run today on Kubernetes, with Postgres, Redis and
Memcached dependencies, in the **same AWS account and region as the CARLOS
-tito boxes** (665106592142, eu-west-1). CI already builds Docker images for
-the k8s deployment; those images are amd64 today, but we *want* arm64 and
-can build arm64 variants as needed — so the PoC standardizes on arm64
-(Graviton boxes, matching the rest of the fleet) rather than inheriting
-x86.
+tito boxes** (665106592142, eu-west-1). Buildkite already builds
+Docker images for the k8s deployment; those images are amd64, and the PoC
+runs them as-is on x86 boxes (Paul, 2026-08-11: these boxes are
+disposable — they'll be turned off after the evaluation, so rebuilding for
+arm64 buys nothing here). arm64 only matters if Rails-on-CARLOS graduates
+to a first-class deployment on the Graviton fleet, and that is recorded as
+roadmap fallout (§7), not PoC work.
CARLOS today has no container runtime and its release format is a single
static binary — a Rails app cannot be a CARLOS *release*. But the edge
@@ -39,8 +41,9 @@ are zero work.
a second control plane pointing away from "simpler all-CARLOS"; Kamal was
rejected because it requires SSH (the fleet is deliberately SSM-only) and
inserts its own proxy.
-- **Reuse the k8s images** — the PoC runs what CI builds, rebuilt for
- arm64; no separate build pipeline, no source checkouts on boxes.
+- **Reuse the k8s images unmodified** — the PoC runs exactly what
+ Buildkite builds (amd64); no separate build pipeline, no arm64 rebuilds,
+ no source checkouts on boxes.
- **Staging first.** Boxes wired to STAGING Postgres/Redis/Memcached now;
production boxes are written in tofu in the same PR but applied only
after the staging PoC convinces us.
@@ -59,9 +62,12 @@ Two new EC2 instances in `platform-infrastructure/tito/`, one `.tf` +
cloud-init file each, copying the svc-pdf shape:
- `rails-classic-staging` and `rails-pro-staging`
-- Graviton (arm64) — e.g. `t4g.large` to start, sized honestly after the
- first week; `lifecycle { ignore_changes = [ami] }` (the floating-AMI
- replace trap is recorded in svc-pdf.tf)
+- x86 (amd64), because that is what the Buildkite images are — `t3a.large`
+ to start (t4g is Graviton-only, so the burstable x86 class is t3/t3a),
+ sized honestly after the first week; an amd64 Debian AMI data source
+ (the fleet's shared AMI is arm64), with
+ `lifecycle { ignore_changes = [ami] }` (the floating-AMI replace trap is
+ recorded in svc-pdf.tf)
- Own security group each; ingress **only** the app port from the IE box's
SG (`module.host_ie.security_group_id`) — same trust boundary as svc-pdf
- IAM: `AmazonSSMManagedInstanceCore` + ECR pull on the Classic/Pro
@@ -90,15 +96,12 @@ The env file carries DATABASE_URL / REDIS_URL / MEMCACHED / SECRET_KEY_BASE
etc., populated once by hand from the k8s staging secrets (values never in
tofu, never in the repo).
-### 3. Images: arm64
+### 3. Images
-The k8s images are amd64 today. The PoC builds arm64 variants of the same
-Dockerfiles (`docker buildx --platform linux/arm64`, pushed to the same ECR
-repos under an `-arm64` tag suffix or a multi-arch manifest). One-off
-manual builds are fine to start; the follow-up is CI building multi-arch so
-the PoC boxes track the same tags k8s deploys. If an image turns out to
-have amd64-only native gems or base layers, fixing that is in scope for the
-PoC — it is exactly the kind of migration work the PoC exists to surface.
+The Buildkite-built amd64 images are used unmodified, pulled from the same
+registry the k8s deployment pulls from. The boxes track the same tags k8s
+staging deploys, so the two deployments stay comparable during the
+parallel run. No rebuilds of any kind for the PoC.
### 4. Dependencies
@@ -165,6 +168,11 @@ the migration evaluation:
boxes; CARLOS logs stage 2 (unit tenants) is the relevant seam.
5. **No health/restart/metrics for off-box upstreams** — `-unit` stamping
only works for processes on the edge box itself.
+6. **Architecture** — the fleet is Graviton; the Rails images are amd64.
+ A first-class Rails-on-CARLOS deployment means arm64 image builds
+ (Buildkite multi-arch). Deferred deliberately: the PoC boxes are
+ disposable, so they run x86 to match the images rather than the other
+ way round.
## Out of scope
@@ -173,7 +181,8 @@ the migration evaluation:
- Cutting any real traffic over from k8s; DNS for the apps' real customer
domains is untouched.
- Production apply (written, not applied).
-- Multi-arch CI (follow-up; one-off buildx builds carry the PoC).
+- Any image rebuilds, including arm64/multi-arch (recorded as roadmap
+ fallout in §7 instead).
## Verification