Cannot specify "root" user to run a job [Kubernetes executor]

Summary

I failed to override USER defined in image and run a job as root on a Kubernetes executor.

Steps to reproduce

The Dockerfile like this:

# test-image
FROM alpine:3.22

RUN \
  adduser -u 1001 -D user1 \
  && adduser -u 1002 -D user2

USER user1

My job is defined as this:

test-job:
  stage: test
  image:
    name: test-image:latest
    pull_policy: always
    entrypoint: [""]
    docker:
      user: root
    kubernetes:
      user: 0
  script:
    - id

This works on the Docker executor, but not on the Kubernetes one! Configuration seems ignored and job is running with default user: user1

And if I try with another user, e.g:

test-job:
  stage: test
  image:
    name: test-image:latest
    pull_policy: always
    entrypoint: [""]
    docker:
      user: root
    kubernetes:
      user: 1002
  script:
    - id

It works on Kubernetes executor, and I can see in my job output: uid=1002(user2) gid=1002(user2) groups=1002(user2)

Another weird behavior is that I cannot define user with a literal name like for Docker executor, e.g:

image:
  kubernetes:
    user: root

Produces this warning in the job output :

WARNING: Error parsing 'uid' or 'gid' from image options, using the configured security context: failed to parse UID strconv.ParseInt: parsing "root": invalid syntax`

And the following error in kube events: Error: failed to create containerd container: mount callback failed on /var/lib/containerd/tmpmounts/containerd-mount3334617909: no users found.

If it's not a bug, it could be nice to add a warning about that in the documentation…

Environment description

We run a self-host version of GitLab (18.2.0) with a premium license on Kubernetes 1.32.

[[runners]]
  executor = "kubernetes"
  [runners.kubernetes]
  poll_interval = 5
  poll_timeout = 1000
  retry_limit = 900
  helper_image = "gitlab/gitlab-runner-helper:alpine3.19-x86_64-v18.1.0"
  allow_privilege_escalation = true
  memory_limit = "1G"
  service_memory_limit = "1G"
  [runners.cache]
    Type = "s3"
    Path = ""
    Shared = true
    [runners.cache.s3]
      ServerAddress = "[redacted]"
      BucketName = "[redacted]"
      BucketLocation = "[redacted]"
      Insecure = false
      AccessKey = "[redacted]"
      SecretKey = "[redacted]"
      MaxUploadedArchiveSize = 5242880000
  [runners.kubernetes.node_selector]
    "xxx.io/role" = "ci"
  [runners.kubernetes.node_tolerations]
      "xxx.io/role=ci" = "NoSchedule"
  [runners.feature_flags]
    FF_USE_FASTZIP = true
    FF_USE_ADVANCED_POD_SPEC_CONFIGURATION=true
    FF_WAIT_FOR_POD_TO_BE_REACHABLE = true
    FF_RETRIEVE_POD_WARNING_EVENTS = true

Used GitLab Runner version

18.1.0

Edited by Maxime Loliée