Building a microservice image on Mac

Hi all,

I’m struggling to build a proper microservice image on MacOS. Switched recently, so I think/hope it’s a stupid little thing missing.

I know it needs to be an amd64 image and I’m on a arm64 CPU. I think QEMU is enabled by default, and accourding to the Web - “if building works, QEMU is enabled”. Well - building works. But not good enough.

Steps:

  1. Build with buildx
❯ docker buildx build -t "python-ms:latest-amd64" --platform linux/amd64 --output "type=tar,dest=image.tar" .
[+] Building 2.0s (12/12) FINISHED                                                                                                                                                                                                                                  docker:desktop-linux
 => [internal] load build definition from Dockerfile                                                                                                                                                                                                                                0.0s
 => => transferring dockerfile: 339B                                                                                                                                                                                                                                                0.0s
 => [internal] load metadata for docker.io/library/python:3.11-slim                                                                                                                                                                                                                 1.1s
 => [internal] load .dockerignore                                                                                                                                                                                                                                                   0.0s
 => => transferring context: 2B                                                                                                                                                                                                                                                     0.0s
 => [1/7] FROM docker.io/library/python:3.11-slim@sha256:139020233cc412efe4c8135b0efe1c7569dc8b28ddd88bddb109b764f8977e30                                                                                                                                                           0.0s
 => => resolve docker.io/library/python:3.11-slim@sha256:139020233cc412efe4c8135b0efe1c7569dc8b28ddd88bddb109b764f8977e30                                                                                                                                                           0.0s
 => [internal] load build context                                                                                                                                                                                                                                                   0.0s
 => => transferring context: 132B                                                                                                                                                                                                                                                   0.0s
 => CACHED [2/7] RUN apt-get update && apt-get -y upgrade                                                                                                                                                                                                                           0.0s
 => CACHED [3/7] COPY requirements.txt /                                                                                                                                                                                                                                            0.0s
 => CACHED [4/7] COPY main /main                                                                                                                                                                                                                                                    0.0s
 => CACHED [5/7] RUN pip install --upgrade pip                                                                                                                                                                                                                                      0.0s
 => CACHED [6/7] RUN pip install --root-user-action=ignore --pre c8y_api                                                                                                                                                                                                            0.0s
 => CACHED [7/7] RUN pip install --root-user-action=ignore -r requirements.txt                                                                                                                                                                                                      0.0s
 => exporting to client tarball                                                                                                                                                                                                                                                     0.9s
 => => sending tarball   
  1. Zipping it
❯ zip -j python-ms.zip cumulocity.json image.tar 
  adding: cumulocity.json (deflated 46%)
  adding: image.tar (deflated 58%)
  1. For reference, the manifest:
❯ jq . < cumulocity.json
{
  "apiVersion": "2",
  "version": "1.0.0",
  "provider": {
    "name": "Cumulocity GmbH"
  },
  "isolation": "MULTI_TENANT",
  "requiredRoles": [
    "ROLE_INVENTORY_ADMIN",
    "ROLE_INVENTORY_READ",
    "ROLE_ALARM_READ",
    "ROLE_ALARM_ADMIN",
    "ROLE_EVENT_READ",
    "ROLE_EVENT_ADMIN",
    "ROLE_USER_MANAGEMENT_ADMIN"
  ],
  "livenessProbe": {
    "httpGet": {
      "path": "/health"
    }
  }
}
  1. Upload fails (using CLI, same error message in UI)
❯ c8y microservices create --file python-ms.zip 
elapsed 00:18   python-ms.zip  [━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━] 100 % 72.93 MiB / 72.93 MiB
2025-07-11T12:02:56.353+0200    ERROR   commandError: failed to upload file. path=python-ms.zip, err=POST https://xyz.eu-latest.cumulocity.com/application/applications/112049/binaries: 500 Push failed on image /Internal Server Error Push failed on image  : /tmp/8243259273465530739/manifest.json (No such file or directory)

Are the build steps OK? I’m pretty certain that I build a image on a Windows machine without any code change and it worked just fine. Didn’t use buildx, but regular build/save, but still? I find the error message particularly weird, maybe the zip is wrong (but it looks ok to me).

Anyways - pointers appreciated!

Cheers, Christoph

I’m pretty sure you’re just using the incorrect output format in your docker buildx command.

I’d have to look up the example commands, however you need to make sure the docker buildx command is doing the equivalent of the following command (as the tarball is an exported “docker” image, and not just a tarball):

docker save $IMAGE_NAME:$TAG_NAME > "image.tar"

update

After doing a quick google search you want something like:

docker buildx build --output type=docker,dest=<output_file.tar> .
1 Like

Yes! This was it. Many thanks for finding this!

1 Like

Not sure if it’s helpful, but how about putting the stuff into actions? Then you don’t need to care about the builds, you just pull and run them locally.

Here’s an example of something I used to build images for ARM and AMD conditionally: microinsight/.github/workflows at main · eickler/microinsight · GitHub

AMD builds are only run for releases. So if I pull an image for local testing on my Mac, I am getting the ARM version that runs natively without eating all my battery on emulation, and I am not eating up GitHub minutes by building all images all the time. It’s become fairly simple now that GitHub supports ARM runners. Earlier, ARM builds took forever due to emulation on GitHub.

Here’s a frame for build, deploying and releasing Python microservices.
https://github.com/Cumulocity-IoT/c8y-ms-gh-python/blob/main/.github/workflows/build-deploy-microservice.yml
It got stuck on my side a bit, but I like using the ready-made tools like release-please.

Cheers,
André

Thanks Andre, I will definitely have a look at that!

I don’t like pushing untested code to much and I’m not sure about wasting cloud CPU “just for testing”, otherwise this would have been my go-to solution.