Ephemeral DigitalOcean Build Pipeline for Kasm Images (GitHub Actions)

Overview

If you are building Kasm images on Apple Silicon, cross-compiling large amd64 workloads (especially Go-heavy stacks) can be slow and painful.

The clean solution is to offload builds to an ephemeral amd64 DigitalOcean droplet from GitHub Actions:

  • Provision droplet on demand
  • Build natively on amd64 (no QEMU emulation)
  • Push image to DigitalOcean Container Registry
  • Destroy droplet every run

This gives you fast, repeatable builds with near-zero infrastructure drift.

Architecture

The pipeline is split into three files:

  1. build-and-push.yml
  2. setup-pipeline.sh
  3. README.md

High-level flow:

  1. Push to main (or run manually)
  2. GitHub Actions creates a temporary DO droplet
  3. Workflow SSHes in and runs a native docker build
  4. Built image is pushed to DOCR
  5. Droplet is destroyed with if: always() cleanup
  6. Optional: register image in Kasm via API (/admin/add_image) and trigger agent pull

Why This Pattern Works

  • Native amd64 performance: no ARM -> x86 emulation overhead
  • Ephemeral infra: every build starts clean, no stale host state
  • Predictable cost: build machine lives only for minutes
  • Scalable builds: choose bigger droplets for heavier compilation jobs

build-and-push.yml (Workflow)

This workflow does the heavy lifting:

  • Trigger: push to main + workflow_dispatch
  • Provision droplet with DO API
  • Poll for public IP and SSH readiness
  • Build and push image from droplet
  • Cleanup droplet unconditionally

You can expose droplet size as a manual input (for example s-2vcpu-4gb to s-8vcpu-16gb) so heavier builds can be accelerated on demand.

Core ideas to include in the workflow

on:
push:
branches: [main]
workflow_dispatch:
inputs:
droplet_size:
description: "DigitalOcean droplet size"
required: true
default: "s-4vcpu-8gb"
# Cleanup must always run
- name: Destroy droplet
if: always()
run: |
curl -sS -X DELETE \
-H "Authorization: Bearer $DO_TOKEN" \
"https://api.digitalocean.com/v2/droplets/$DROPLET_ID"

Keep Kasm registration as a separate job, gated behind secrets and usually dependent on successful push.

setup-pipeline.sh (Bootstrap Script)

Use a one-shot setup script to avoid manual secret drift. The script should:

  1. Create/verify DO Container Registry
  2. Generate SSH keypair for CI
  3. Register public key in DigitalOcean account
  4. Set all required GitHub repository secrets

Run it like:

Terminal window
GITHUB_REPO=your-org/your-repo ./setup-pipeline.sh

Typical secrets configured:

  • DO_TOKEN
  • DO_REGISTRY_NAME
  • DO_SSH_PRIVATE_KEY
  • DO_SSH_KEY_ID
  • DO_REGION

Then add Kasm secrets later for v2.0:

  • KASM_BASE_URL
  • KASM_API_KEY
  • KASM_API_SECRET

README.md (Operational Docs)

Your repo docs should cover:

  • Pipeline architecture diagram
  • Secret inventory
  • One-time setup instructions
  • Manual run options (droplet size)
  • Failure modes + troubleshooting
  • Cost model

Cost and Runtime

For short-lived CI builds (roughly 10-15 minutes), cost is usually very low (often around $0.01-$0.02 per run, depending on size/region and image pull/build time).

You only pay while the droplet exists, so strict cleanup logic is critical.

Kasm v2.0 Registration (Ready but Optional)

Once image pushes are stable, enable the Kasm integration step:

  1. Call Kasm API endpoint to add/register image metadata
  2. Trigger Kasm agents to pull new image tag
  3. Validate image appears in Kasm catalog

Recommended rollout:

  1. Start with build + push only
  2. Add Kasm registration in dry-run or staging
  3. Enable in production after first successful end-to-end tests

Hardening Recommendations

  • Use unique immutable tags (for example 2026.03.01-<sha>)
  • Avoid latest as deployment source of truth
  • Use least-privilege DO tokens
  • Rotate CI SSH keys periodically
  • Add timeout guards for droplet provisioning + SSH wait
  • Emit droplet ID and cleanup status in logs for auditability

Final Thoughts

This pattern is one of the cleanest ways to solve ARM/x86 build friction while keeping CI reproducible and cheap. You get native amd64 speed when you need it, without maintaining permanent builder infrastructure.