Running Linux Containers on a Cloud Mac: Bridging iOS and Backend CI with Colima

DevOps & CI/CD ·~6 min read

Running Linux Containers on a Cloud Mac: Bridging iOS and Backend CI with Colima

Your repo holds both an iOS app and its backend service: the backend uses docker-compose to spin up a database, a message queue, and a handful of microservices, and integration tests need to actually connect to those containers to run. For the iOS side you rented a cloud Mac mini from HireVPS to do Xcode builds, and everything worked fine — until you tried to make CI run a backend integration pass on the same box, and discovered there's no native Linux kernel on Apple Silicon. docker run just errors out saying it can't find the daemon. That's not a config mistake, it's an architectural fact: Docker on macOS has always run inside a "borrowed" environment.

Why you'd even want Linux containers on a cloud Mac

Monorepos are increasingly common, and a single PR might touch both the iOS client and the backend API at once — testing only the iOS side simply won't catch a protocol mismatch. The ideal pipeline looks like this: on the same runner, build and unit-test with Xcode first, then bring up the backend dependencies defined in docker-compose, run a round of end-to-end integration tests, and report everything together. The upside is obvious — no need to maintain two separate CI environments, and no need to punch holes in network policy for cross-machine communication. The one requirement is that this cloud Mac has to be able to run Linux containers.

There's no native Linux kernel on Apple Silicon, so any "run Docker" setup is really just booting a hidden Linux VM under the hood first — and performance comes down almost entirely to how much CPU and memory that VM gets.

Setup: picking a lightweight virtualization approach

There are two main paths to running containers on macOS: Docker Desktop, and Colima paired with the native docker CLI. Both boot a stripped-down Linux VM on top of Apple's Virtualization.framework under the hood — they differ in form factor and the use cases they suit.

Docker Desktop vs. Colima

Aspect Docker Desktop Colima
Interface GUI + background daemon CLI only
Startup Manual, or auto-launch at login One command in a script
Resource footprint Heavier, several persistent processes Lightweight, starts/stops on demand
Best fit Day-to-day local debugging Unattended CI runners
Licensing Paid license required at enterprise scale Open source, free

For CI running on an unattended runner, Colima is clearly the better fit: it doesn't depend on a graphical session, can be brought up with a single command in a shell script, and shuts down cleanly afterward — no orphaned processes left hogging memory.

Installation and resource tuning

On the cloud Mac mini, start by installing the toolchain with Homebrew:

brew install colima docker docker-compose docker-buildx

colima start \
  --cpu 4 \
  --memory 12 \
  --disk 60 \
  --vm-type=vz \
  --mount-type=virtiofs

docker context use colima
docker compose -f docker-compose.ci.yml up -d --wait

--vm-type=vz selects Apple's native virtualization backend (noticeably faster than the older QEMU mode), and --mount-type=virtiofs gets the mounted project directory close to native disk I/O speed inside the container. Skip either flag and both build speed and file I/O take a visible hit.

There's no universal formula for resource allocation, but a reasonable starting point: give the Colima VM 50%–70% of the host's memory, and leave roughly half the CPU cores free for Xcode builds. Mapped against instance memory, a rough starting table looks like this (always confirm the current available specs in the console):

Instance memory Suggested Colima memory Suggested Colima CPU
16GB 6GB 2 cores
32GB 12GB 4 cores
48GB 20GB 6 cores
64GB 28GB 8 cores

If Xcode builds and Colima run at the same time, over-allocating memory to either side causes contention and swapping, which paradoxically makes builds slower. It's worth running the full pipeline a few times before wiring it into CI, just to dial in that balance.

Wiring it into CI: making the self-hosted runner Linux-container-aware

If the runner is a GitHub Actions self-hosted runner, just add a step that starts Colima, runs the tests, and tears everything down afterward — no extra service-container declarations needed:

jobs:
  hybrid-ci:
    runs-on: [self-hosted, macos, cloud-mac]
    steps:
      - uses: actions/checkout@v4
      - name: Xcode build & unit test
        run: |
          xcodebuild -scheme App -destination "platform=iOS Simulator,name=iPhone 15" test
      - name: Bring up backend containers
        run: |
          colima start --cpu 4 --memory 12 --vm-type=vz --mount-type=virtiofs
          docker compose -f docker-compose.ci.yml up -d --wait
      - name: Integration tests
        run: ./scripts/run-integration-tests.sh
      - name: Teardown
        if: always()
        run: |
          docker compose -f docker-compose.ci.yml down -v
          colima stop

That if: always() step matters: containers and the VM need to be torn down even when tests fail, otherwise a long-rented cloud Mac accumulates orphaned VMs over time, eating memory and slowing down the next build.

Caching strategy: reusing image layers and dependencies

If the Colima VM re-pulls images and reinstalls dependencies from scratch every time it restarts, CI runtime balloons. Two simple, effective habits fix that:

  1. Persist image layers to disk: don't run colima delete between runs — stick to colima stop/colima start. The VM's disk (by default under ~/.colima) keeps already-pulled image layers, so a restart doesn't mean re-downloading everything.
  2. Mount build-cache volumes: declare named volumes for dependency directories like node_modules, vendor, or .cargo in your backend Dockerfiles. Combined with docker compose's --wait, the first build is a bit slower, but subsequent incremental builds save the bulk of that time.

Combined with the virtiofs mount mentioned earlier, project-directory I/O generally stops being a bottleneck — the thing actually worth watching is the network layer. If integration tests hit external APIs, make sure the test environment points to a mock or sandbox endpoint, not silently against production.

Pitfalls and a checklist

Common issues people hit when wiring this up, roughly in priority order:

Once this pipeline is smoothed out, the same cloud Mac mini can double as both the iOS build machine and the backend integration test environment, with no need to juggle cross-machine networking or credentials. Submit a PR, and one pipeline gives you a complete pass/fail signal.

Frequently asked questions

Can Apple Silicon cloud Macs run Docker natively?

No. Docker Desktop and Colima both spin up a hidden Linux virtual machine via Virtualization.framework, and containers actually run inside that VM, so performance depends entirely on the CPU and RAM you allocate to it.

Colima or Docker Desktop for CI?

Pick Colima for CI: it's a CLI tool with a small footprint and fast startup, ideal for scripted boot inside an unattended runner. Docker Desktop suits local GUI debugging but isn't recommended on headless runners.

How should I size Colima's VM across different RAM tiers?

As a rule of thumb, give Colima's VM 50%–70% of host RAM — e.g. a 32GB host can allocate 12GB and 4 cores to Colima. Confirm the exact configuration currently available in the control panel, then tune it under load.

HireVPS

Try a dedicated cloud Mac mini today

Rent by the day, get SSH/VNC credentials in 2 minutes, and upgrade your configuration anytime.

Rent a Mac mini now