Skip to content

Commit 0648cc2

Browse files
committed
feat: add Podman support as drop-in replacement for Docker
Podman (v4.0+) is now supported alongside Docker as the local container runtime. The runtime is auto-detected at make time; no aliasing or manual configuration is required. Existing Docker-based workflows are unchanged. ## What changed **Runtime detection (`scripts/detect-runtime.sh`)** - New sourced helper exports `CONTAINER_RUNTIME`, `IS_PODMAN`, and `COMPOSE_CMD` based on which binary is on `PATH`. - Explicit override via `CONTAINER_RUNTIME=docker|podman` is honoured. - Sets `KIND_EXPERIMENTAL_PROVIDER=podman` automatically when Podman is active. **Podman VM setup (`scripts/setup-podman-vm.sh`)** - On macOS/Windows: creates a rootful Podman machine (4 CPU / 8 GB / 60 GB) if one does not already exist, starts it, loads the NFS kernel modules (`nfs`, `nfsv3`, `nfsd`) required by the NFS volume broker, persists them across reboots via a systemd unit, and raises inotify limits. - On Linux: skips VM creation, loads NFS modules on the host directly, and validates that rootless Podman can bind privileged ports (80/443/ 2222) via `net.ipv4.ip_unprivileged_port_start`. **kind config (`kind-podman.yaml`)** - Separate kind cluster config for Podman: keeps kindnet as the CNI (Cilium is skipped — see below), mounts `/sys/fs/bpf` into nodes with `HostToContainer` propagation, and uses the same port mappings as the Docker config. **Cilium handling (`assets/values/cilium-podman.yaml`, `helmfile.yaml.gotmpl`)** - Cilium 1.18.x probes `BPF_PROG_TYPE_CGROUP_SOCK` unconditionally at startup, which requires `CAP_NET_ADMIN` in the host user namespace — unavailable in rootless Podman. Cilium is therefore skipped (`installed: false`) when `SKIP_CILIUM=true`. - `assets/values/cilium-podman.yaml` provides a privilege-stripped Cilium config for environments where Cilium *is* installed over Podman (e.g. rootful mode): disables eBPF masquerade, socket LB, host firewall, session affinity, Hubble, and all BPF auto-mount, using VXLAN tunnel + iptables masquerade instead. **Makefile** - `install` target detects Podman and sets `SKIP_CILIUM=true` + passes `CILIUM_EXTRA_VALUES` / `SKIP_CILIUM` into the helmfile container. - New `build` target wraps `docker buildx bake` and exits with a clear error when Podman is active (bake is Docker-only; use `podman build` directly for local image development). **CI (`kind-smoke.yaml`)** - Smoke workflow extended with a Podman job that installs Podman, configures the required kernel tunables, and runs the full `make up` + smoke test suite against Podman. **Documentation (`README.md`)** - Added a *Podman Support* section covering Linux (rootless), macOS/ Windows (Podman Desktop), and known limitations (no `make build`, Cilium disabled, ARM/Apple Silicon notes). **Dependency bumps (bundled)** - helmfile v1.5.0 → v1.5.1 - cloudfoundry/cf-deployment v56.1.0 → v56.5.0 - Multiple CF release version bumps (capi, diego, routing, loggregator, nfs-volume, cf-networking, bosh-dns, uaa, credhub, k8s-garden-client, k8s-policy-agent) - Debian base image: trixie-20260505 → trixie-20260518 - nginx: v1.30 → v1.31.0 - Renovate action: v46.1.13 → v46.1.14 ## Non-breaking guarantees - All existing `make up / install / login / create-org / bootstrap` targets work identically when Docker is the detected runtime. - `CONTAINER_RUNTIME=docker` forces Docker regardless of what is installed, preserving any automation that relies on Docker explicitly.
1 parent c114258 commit 0648cc2

37 files changed

Lines changed: 947 additions & 296 deletions

.github/renovate.json

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,6 @@
2323
"extractVersionTemplate": "^v?(?<version>.*)$"
2424
}
2525
],
26-
"helmfile": {
27-
"enabled": true,
28-
"registryAliases": {
29-
"nats": "https://nats-io.github.io/k8s/helm/charts",
30-
"istio": "https://istio-release.storage.googleapis.com/charts",
31-
"bitnami": "https://charts.bitnami.com/bitnami",
32-
"cloudfoundry-k8s": "oci://ghcr.io/cloudfoundry/helm"
33-
}
34-
},
3526
"helm-values": {
3627
"enabled": false
3728
}

.github/workflows/kind-cats.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ jobs:
3131
--privileged \
3232
--pid host \
3333
-v /lib/modules:/lib/modules:ro \
34-
alpine sh -c "sysctl -w fs.inotify.max_user_instances=512 && modprobe nfs && modprobe nfsd"
34+
alpine sh -c "sysctl -w fs.inotify.max_user_instances=512 && sysctl -w net.ipv4.ip_unprivileged_port_start=80 && modprobe nfs && modprobe nfsd"
3535
- name: Install dependencies
3636
if: steps.check_changes.outputs.skip != 'true'
3737
run: |
@@ -48,7 +48,7 @@ jobs:
4848
# renovate: dataSource=github-releases depName=kubernetes-sigs/kind
4949
KIND_VERSION: "0.31.0"
5050
# renovate: dataSource=github-releases depName=helmfile/helmfile
51-
HELMFILE_VERSION: "1.5.0"
51+
HELMFILE_VERSION: "1.5.1"
5252
- name: Run make up
5353
if: steps.check_changes.outputs.skip != 'true'
5454
run: |

.github/workflows/kind-smoke.yaml

Lines changed: 144 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,8 @@ jobs:
4242
- name: Set kernel settings
4343
if: steps.check_changes.outputs.skip != 'true'
4444
run: |
45-
docker run --rm \
46-
--privileged \
47-
--pid host \
48-
alpine sh -c "sysctl -w fs.inotify.max_user_instances=512"
45+
sudo sysctl -w fs.inotify.max_user_instances=512
46+
sudo sysctl -w net.ipv4.ip_unprivileged_port_start=80
4947
- name: Install dependencies
5048
if: steps.check_changes.outputs.skip != 'true'
5149
run: |
@@ -62,15 +60,18 @@ jobs:
6260
# renovate: dataSource=github-releases depName=kubernetes-sigs/kind
6361
KIND_VERSION: "0.31.0"
6462
# renovate: dataSource=github-releases depName=helmfile/helmfile
65-
HELMFILE_VERSION: "1.5.0"
63+
HELMFILE_VERSION: "1.5.1"
6664
- name: Run make up (default)
6765
if: steps.check_changes.outputs.skip != 'true' && github.event_name == 'pull_request'
6866
run: make up
67+
env:
68+
CONTAINER_RUNTIME: docker
6969
- name: Run make up
7070
if: steps.check_changes.outputs.skip != 'true' && github.event_name != 'pull_request'
7171
run: make up
7272
env:
73-
INSTALL_OPTIONAL_COMPONENTS: ${{ inputs.minimal }}
73+
CONTAINER_RUNTIME: docker
74+
INSTALL_OPTIONAL_COMPONENTS: ${{ inputs.minimal }}
7475
- name: Login
7576
if: steps.check_changes.outputs.skip != 'true'
7677
run: make login
@@ -119,3 +120,140 @@ jobs:
119120
with:
120121
name: report.json
121122
path: ./cf-smoke-tests/report.json
123+
124+
kind-smoke-podman:
125+
runs-on: ubuntu-latest
126+
permissions:
127+
id-token: write
128+
contents: read
129+
steps:
130+
- uses: actions/checkout@v6
131+
with:
132+
fetch-depth: 0
133+
- name: Check if only ignored paths changed
134+
id: check_changes
135+
run: |
136+
if [ "${{ github.event_name }}" == "pull_request" ]; then
137+
CHANGED_FILES=$(git diff --name-only ${{ github.event.pull_request.base.sha }} ${{ github.sha }})
138+
RELEVANT_FILES=$(echo "$CHANGED_FILES" | grep -v -E '.*\.Dockerfile|^releases/.*/files/|^\.github/|^docs/|\.md$|^renovate\.json$' || true)
139+
if [ -z "$RELEVANT_FILES" ]; then
140+
echo "Only ignored paths were changed. Skipping workflow."
141+
echo "skip=true" >> $GITHUB_OUTPUT
142+
else
143+
echo "skip=false" >> $GITHUB_OUTPUT
144+
fi
145+
else
146+
echo "skip=false" >> $GITHUB_OUTPUT
147+
fi
148+
- name: Set kernel settings
149+
if: steps.check_changes.outputs.skip != 'true'
150+
run: |
151+
sudo sysctl -w fs.inotify.max_user_instances=512
152+
sudo sysctl -w net.ipv4.ip_unprivileged_port_start=80
153+
# Mount BPF filesystem on the host (needed by kindnet on some kernels)
154+
sudo mount bpffs /sys/fs/bpf -t bpf -o nosuid,nodev,noexec,relatime || true
155+
- name: Install podman-compose
156+
if: steps.check_changes.outputs.skip != 'true'
157+
run: |
158+
sudo apt-get update -qq
159+
sudo apt-get install -y podman-compose
160+
# Ensure podman-compose is used instead of the docker-compose plugin shim
161+
sudo ln -sf /usr/bin/podman-compose /usr/local/bin/podman-compose
162+
- name: Install dependencies
163+
if: steps.check_changes.outputs.skip != 'true'
164+
run: |
165+
mkdir -p $HOME/.local/bin && echo "$HOME/.local/bin" >> "$GITHUB_PATH"
166+
curl -L https://kind.sigs.k8s.io/dl/v${KIND_VERSION}/kind-linux-amd64 -o $HOME/.local/bin/kind
167+
chmod +x $HOME/.local/bin/kind
168+
curl -L https://github.com/helmfile/helmfile/releases/download/v${HELMFILE_VERSION}/helmfile_${HELMFILE_VERSION}_linux_amd64.tar.gz | tar -zx
169+
mv helmfile $HOME/.local/bin/helmfile
170+
curl -L "https://packages.cloudfoundry.org/stable?release=linux64-binary&version=${CF_CLI_VERSION}&source=github" | tar -zx
171+
mv cf8 $HOME/.local/bin/cf
172+
env:
173+
# renovate: dataSource=github-releases depName=cloudfoundry/cli
174+
CF_CLI_VERSION: "8.18.3"
175+
# renovate: dataSource=github-releases depName=kubernetes-sigs/kind
176+
KIND_VERSION: "0.31.0"
177+
# renovate: dataSource=github-releases depName=helmfile/helmfile
178+
HELMFILE_VERSION: "1.5.1"
179+
- name: Run make up (default)
180+
if: steps.check_changes.outputs.skip != 'true' && github.event_name == 'pull_request'
181+
run: make up
182+
env:
183+
CONTAINER_RUNTIME: podman
184+
- name: Run make up
185+
if: steps.check_changes.outputs.skip != 'true' && github.event_name != 'pull_request'
186+
run: make up
187+
env:
188+
CONTAINER_RUNTIME: podman
189+
INSTALL_OPTIONAL_COMPONENTS: ${{ inputs.minimal }}
190+
- name: Login
191+
if: steps.check_changes.outputs.skip != 'true'
192+
run: make login
193+
- name: Init
194+
if: steps.check_changes.outputs.skip != 'true'
195+
run: make bootstrap-complete
196+
- name: setup CF tests
197+
if: steps.check_changes.outputs.skip != 'true'
198+
uses: ./.github/actions/setup-cf-tests
199+
with:
200+
test-repo: cf-smoke-tests
201+
test-branch: main
202+
config-template: ./.github/smoke-config.tpl
203+
config-output: ./.github/smoke-config.json
204+
- name: run smoke test
205+
if: steps.check_changes.outputs.skip != 'true'
206+
env:
207+
CONFIG: "${{ github.workspace }}/.github/smoke-config.json"
208+
GINKGO_NO_COLOR: "true"
209+
run: |
210+
./cf-smoke-tests/bin/test --no-color --github-output --timeout=30m --procs=4 --json-report report.json
211+
- name: debug events
212+
if: failure()
213+
run: kubectl get events -A --sort-by='.lastTimestamp'
214+
- name: debug cilium
215+
if: failure()
216+
run: |
217+
echo "===== CILIUM PODS ====="
218+
kubectl get pod -n kube-system -l k8s-app=cilium -o wide
219+
echo "===== CILIUM AGENT LOGS (all pods) ====="
220+
for pod in $(kubectl get pod -n kube-system -l k8s-app=cilium -o jsonpath='{.items[*].metadata.name}'); do
221+
echo "--- $pod ---"
222+
kubectl logs -n kube-system "$pod" --all-containers=true --previous 2>/dev/null || kubectl logs -n kube-system "$pod" --all-containers=true 2>/dev/null || true
223+
done
224+
echo "===== CILIUM OPERATOR LOGS ====="
225+
kubectl logs -n kube-system -l name=cilium-operator --all-containers=true 2>/dev/null || true
226+
echo "===== CILIUM STATUS ====="
227+
kubectl exec -n kube-system $(kubectl get pod -n kube-system -l k8s-app=cilium -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) -- cilium status 2>/dev/null || true
228+
- name: debug pods
229+
if: failure()
230+
run: |
231+
echo "===== CLOUD_CONTROLLER ====="
232+
kubectl get pod -l app.kubernetes.io/name=cloud-controller -o wide
233+
kubectl get pod -l app.kubernetes.io/name=cloud-controller -o jsonpath='{.status.containerStatuses[*].state}' | jq
234+
kubectl logs -l app.kubernetes.io/name=cloud-controller --all-containers=true
235+
echo "===== CC_WORKER ====="
236+
kubectl get pod -l app.kubernetes.io/name=cc-worker -o wide
237+
kubectl logs -l app.kubernetes.io/name=cc-worker
238+
echo "===== CC_UPLOADER ====="
239+
kubectl get pod -l app=cc-uploader -o wide
240+
kubectl logs -l app=cc-uploader
241+
echo "===== CC_DEPLOYMENT_UPDATER ====="
242+
kubectl get pod -l app.kubernetes.io/name=cc-deployment-updater -o wide
243+
kubectl logs -l app.kubernetes.io/name=cc-deployment-updater
244+
echo "===== CC_WORKER_CLOCK ====="
245+
kubectl get pod -l app.kubernetes.io/name=cc-worker-clock -o wide
246+
kubectl logs -l app.kubernetes.io/name=cc-worker-clock
247+
echo "===== CF_TCP_ROUTER ====="
248+
kubectl get pod -n cf-system -l app=cf-tcp-router -o wide
249+
kubectl get pod -n cf-system -l app=cf-tcp-router -o jsonpath='{.items[*].status.containerStatuses[*].state}' | jq 2>/dev/null || true
250+
kubectl logs -n cf-system -l app=cf-tcp-router --all-containers=true 2>/dev/null || true
251+
echo "===== ROUTING_API ====="
252+
kubectl get pod -n cf-system -l app=routing-api -o wide
253+
kubectl logs -n cf-system -l app=routing-api --all-containers=true 2>/dev/null || true
254+
- uses: actions/upload-artifact@v7
255+
if: always()
256+
with:
257+
name: report-podman.json
258+
path: ./cf-smoke-tests/report.json
259+

.github/workflows/renovate.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ jobs:
2626
steps:
2727
- uses: actions/checkout@v6
2828
- name: Self-hosted Renovate
29-
uses: renovatebot/github-action@v46.1.13
29+
uses: renovatebot/github-action@v46.1.14
3030
with:
3131
token: ${{ secrets.SERVICE_USER_GITHUB_TOKEN }}
3232
configurationFile: .github/renovate-config.js

.github/workflows/sync-cf-deployment-versions.yaml

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,32 @@ jobs:
1111
permissions:
1212
contents: write
1313
pull-requests: write
14+
env:
15+
BRANCH: auto/sync-cf-deployment-versions
1416
steps:
1517
- name: Checkout repository
1618
uses: actions/checkout@v6
1719
with:
1820
ref: main
21+
fetch-depth: 0
1922
token: ${{ secrets.SERVICE_USER_GITHUB_TOKEN }}
2023

24+
- name: Prepare sync branch
25+
run: |
26+
git config user.name "ard-wg-k8s-gitbot"
27+
git config user.email "kind-deployment@cloudfoundry.org"
28+
29+
if git fetch origin "$BRANCH" 2>/dev/null; then
30+
git checkout "$BRANCH"
31+
if ! git rebase main; then
32+
git rebase --abort
33+
echo "::error::Rebase failed due to conflicts, manual resolution required."
34+
exit 1
35+
fi
36+
else
37+
git checkout -b "$BRANCH"
38+
fi
39+
2140
- name: Get latest cf-deployment release tag
2241
id: cf-deployment-tag
2342
env:
@@ -27,26 +46,23 @@ jobs:
2746
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
2847
echo "Latest cf-deployment release: $TAG"
2948
30-
- name: Extract release versions and generate versions.yaml
49+
- name: Extract release versions and update charts
3150
env:
3251
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
3352
run: |
3453
pip3 install pyyaml requests
3554
python3 scripts/sync-cf-deployment-versions.py
3655
37-
- name: Create pull request
56+
- name: Commit and push
3857
env:
3958
GH_TOKEN: ${{ secrets.SERVICE_USER_GITHUB_TOKEN }}
4059
run: |
41-
git config user.name "ard-wg-k8s-gitbot"
42-
git config user.email "kind-deployment@cloudfoundry.org"
43-
git checkout -b auto/sync-cf-deployment-versions
4460
if git diff --quiet; then
45-
echo "changed=false" >> "$GITHUB_OUTPUT"
61+
echo "No changes to commit."
4662
exit 0
4763
fi
4864
4965
git add -A
5066
git commit -m "chore: update BOSH release versions from cf-deployment ${{ steps.cf-deployment-tag.outputs.tag }}"
51-
git push -f -u origin auto/sync-cf-deployment-versions
52-
gh pr create --fill --base main || echo "PR already exists"
67+
git push --force-with-lease -u origin "$BRANCH"
68+
gh pr create --fill --base main || true

Makefile

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,33 @@
11
LOCAL = true
22
TARGET_ARCH ?= $(if $(filter true,$(LOCAL)),$(shell go env GOARCH),amd64)
33
# renovate: dataSource=github-releases depName=helmfile/helmfile
4-
HELMFILE_VERSION ?= "1.5.0"
4+
HELMFILE_VERSION ?= "1.5.1"
5+
6+
# Build all images for the local architecture (arm64 on Apple Silicon, amd64 elsewhere).
7+
# This ensures Go binaries like storage-cli are native – not run under Rosetta,
8+
# which causes 'taggedPointerPack' panics with high memory addresses.
9+
build:
10+
@ . ./scripts/detect-runtime.sh; \
11+
if [ "$$CONTAINER_RUNTIME" = "podman" ]; then \
12+
echo "Building with Podman is not yet supported via docker-bake.hcl."; \
13+
echo "Use 'podman build' manually with the Dockerfiles in releases/."; \
14+
exit 1; \
15+
fi; \
16+
docker buildx bake --file docker-bake.hcl --set "*.platform=linux/$(TARGET_ARCH)" $(BAKE_TARGETS)
517

618
init: temp/certs/ca.key temp/certs/ca.crt temp/certs/ssh_key temp/certs/ssh_key.pub temp/secrets.sh temp/secrets.env
719

820
temp/certs/ca.key temp/certs/ca.crt temp/certs/ssh_key temp/certs/ssh_key.pub temp/secrets.sh temp/secrets.env:
921
@ ./scripts/init.sh
1022

1123
install:
12-
kind get kubeconfig --name cfk8s > temp/kubeconfig
13-
docker run --rm --net=host --env-file temp/secrets.env \
24+
@ . ./scripts/detect-runtime.sh; \
25+
if [ "$$IS_PODMAN" = "true" ]; then export SKIP_CILIUM="true"; fi; \
26+
kind get kubeconfig --name cfk8s > temp/kubeconfig; \
27+
$$CONTAINER_RUNTIME run --rm --net=host --env-file temp/secrets.env \
1428
--env INSTALL_OPTIONAL_COMPONENTS \
29+
--env CILIUM_EXTRA_VALUES \
30+
--env SKIP_CILIUM \
1531
-v "$$PWD/temp/certs:/certs" -v "$$PWD/temp/kubeconfig:/helm/.kube/config:ro" -v "$$PWD:/wd" --workdir /wd ghcr.io/helmfile/helmfile:v$(HELMFILE_VERSION) helmfile sync
1632

1733
login:
@@ -33,7 +49,7 @@ create-org:
3349
bootstrap: create-org
3450
@ ./scripts/upload_buildpacks.sh
3551

36-
bootstrap-complete: create-org
52+
bootstrap-complete: create-org
3753
@ ALL_BUILDPACKS=true ./scripts/upload_buildpacks.sh
3854

3955
up: create-kind init install

README.md

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,16 @@ This repository provides a simple and fast way to run Cloud Foundry locally. It
66

77
The following tools need to be installed:
88

9-
- [`docker`](https://docs.docker.com/engine/install/)
9+
- [`docker`](https://docs.docker.com/engine/install/) or [`podman`](https://podman.io/docs/installation) (v4.0 or higher)
10+
- [`docker-compose`](https://docs.docker.com/compose/install) or [`podman-compose`](https://github.com/containers/podman-compose) (v1.0 or higher)
1011
- [`kind`](https://kind.sigs.k8s.io/docs/user/quick-start/#installing-from-release-binaries) (v0.31.0 or higher)
1112
- [`kubectl`](https://kubernetes.io/docs/tasks/tools/#kubectl) (v1.35.1 or higher)
1213
- `make`:
1314
- It should be already installed on MacOS and Linux.
1415
- For Windows installation see: <https://gnuwin32.sourceforge.net/packages/make.htm>
1516

17+
The container runtime is auto-detected. Set `CONTAINER_RUNTIME=docker` or `CONTAINER_RUNTIME=podman` to override.
18+
1619
## Run the Installation
1720

1821
```bash
@@ -48,6 +51,40 @@ You can configure the installation by setting the environment variable `INSTALL_
4851

4952
`bosh-dns`, `cf-tcp-router`, `credhub`, `loggregator`, `nfsbroker`, `policy-agent`, `policy-server`, `routing-api`, `service-discovery-controller`
5053

54+
## Podman Support
55+
56+
Podman is supported as a drop-in replacement for Docker. The runtime is detected automatically; no aliasing is required.
57+
58+
### Linux (rootless Podman)
59+
60+
Rootless Podman is fully supported on Linux. The following kernel settings must be applied before running `make up` — either manually or via your system's sysctl configuration:
61+
62+
```bash
63+
sudo sysctl -w fs.inotify.max_user_instances=512
64+
sudo sysctl -w net.ipv4.ip_unprivileged_port_start=80
65+
```
66+
67+
The first setting prevents inotify exhaustion under heavy workloads. The second allows the kind node containers and pods to bind privileged ports (80, 443, 2222) without root.
68+
69+
### macOS / Windows (Podman Desktop)
70+
71+
A Podman machine is created and configured automatically by `make up`. The machine is created in rootful mode with 4 CPUs, 8 GB RAM, and 60 GB disk. No manual configuration is needed.
72+
73+
### Limitations
74+
75+
- **CNI:** Cilium is skipped under rootless Podman (Linux CI / rootless desktop) because Cilium 1.18.x requires `CAP_NET_ADMIN` in the host user namespace, which rootless containers cannot provide. [kindnet](https://github.com/aojea/kindnet) is used instead, providing full pod-to-pod connectivity without eBPF privileges. Cilium network policies are therefore not enforced in this mode.
76+
- **Image builds:** `make build` (which uses `docker buildx bake`) is not supported with Podman. Use `podman build` directly with the Dockerfiles in `releases/` for local image development.
77+
78+
## ARM / Apple Silicon Limitations
79+
80+
The CF stack (`cflinuxfs4`) and all buildpacks are **x86-64 (amd64) only**. CF applications run inside `cflinuxfs4` rootfs containers, which are amd64 images and require x86 emulation on ARM hosts.
81+
82+
- **Docker Desktop on Apple Silicon:** Enable Rosetta emulation (Settings → General → Use Rosetta for x86_64/amd64 emulation). This is the recommended and well-tested path.
83+
- **Podman on Apple Silicon:** The Podman machine is created with `--rootful` and runs under QEMU/Rosetta. Functional, but noticeably slower than Docker Desktop with Rosetta.
84+
- **Linux ARM64:** Not supported. The `cflinuxfs4` stack image and pre-compiled buildpack zip files are amd64-only. CF app staging and execution will fail on a native ARM64 host without kernel-level x86 emulation (`binfmt_misc` with QEMU).
85+
86+
The CF platform components themselves (gorouter, diego, CAPI, etc.) are built for the local architecture (`make build` targets the native arch), so control-plane operations are native-speed on ARM. Only the **application workload layer** (buildpacks, cflinuxfs4 rootfs) is restricted to amd64.
87+
5188
## Unsupported Features
5289

5390
- Routing isolation segments are not fully feature complete since this relies on more than one gateway which is not possible to realize in a local kind setup (see [FAQ](./docs/faq.md))
@@ -61,4 +98,4 @@ You can configure the installation by setting the environment variable `INSTALL_
6198

6299
Please check our [contributing guidelines](/CONTRIBUTING.md).
63100

64-
This project follows [Cloud Foundry Code of Conduct](https://www.cloudfoundry.org/code-of-conduct/).
101+
This project follows [Cloud Foundry Code of Conduct](https://www.cloudfoundry.org/code-of-conduct/)

0 commit comments

Comments
 (0)