Skip to content

Support Runner inside of Docker Container #406

Description

@jpb

Describe the enhancement

Fully support all features when runner is within a Docker container.

Not all features are currently supported when the runner is within a Docker container, specifically those features that use Docker like Docker-based Actions and services. Running self-hosted runners using Docker is an easy way to scale out runners on some sort of Docker-based cluster and an easy way to provide clean workspaces for each run (with ./run.sh --once).

Code Snippet

Possible implementation that I am using now.

Additional information

There are a few areas of concern when the runner executes in a Docker container:

  1. Filesystem access for other containers needed as part of the job. This can be resolved by using a volume mount from the host which uses a matching host and container path (for example: docker run -v /home/github:/home/github, although it doesn't have to be this exact directory) and telling the runner to use a directory within that for the work directory (./config.sh --work /home/github/work). This works with the current volume mounting behaviour for containers created by the runner. This would need to be documented as part of the setup process for a Docker-based runner.
  2. Network between runner and other containers needed as part of the job. This could be resolved by not creating a network as part of the run and instead optionally accepting an existing network to be used. I have found that it works well to use --network container:<container ID of the runner> to reuse the network from the runner container without having to orchestrate a network created via docker network create. There is no straightforward way to discover the network or ID of a container from within it, so it would likely need to be the responsibility of the user to pass this information to the runner (I current do something like "container:$(cat /proc/self/cgroup | grep "cpu" | head -n 1 | rev | cut -d/ -f 1 | rev)" from within the runner container to find the ID and pass this to the runner, although this isn't guaranteed to work in all cases).

Activity

  1. jpb commented on Apr 7, 2020

    @jpb
    Author

    There appear to be a couple more things that need to be done to account for multiple runners on the same host concurrently:

    1. docker network prune can not run concurrently and should likely be retried if such an error is received:
      /usr/local/bin/docker network prune --force --filter "label=898d1dec6adc"
      Error response from daemon: a prune operation is already running
      ##[warning]Delete stale container networks failed, docker network prune fail with exit code 1
      
    2. The docker label is not sufficient for isolating separate runners on the same host. The current hash of the root directory will result in the same label being used for all runners with the exact same version. In my testing I've switched this to use the hostname, but perhaps something like the runner name or run ID could be used.
  2. jpb commented on May 13, 2020

    @jpb
    Author

    @TingluoHuang @bryanmacfarlane I'm hoping to get your feedback on this – getting official support for this would be a huge help for me. I'm happy to work on an implementation if that is helpful.

  3. SonicGD commented on Jun 8, 2020

    @SonicGD

    This is a big problem for us. We want to run gh runner in docker to easier scaling and isolation. But we need to also run services for tests. So our workaround for now is to run multiple runners on host, but scaling container with docker-compose is so much easier and convenient.

  4. npalm commented on Jun 8, 2020

    @npalm

    This is a big problem for us. We want to run gh runner in docker to easier scaling and isolation. But we need to also run services for tests. So our workaround for now is to run multiple runners on host, but scaling container with docker-compose is so much easier and convenient.

    Fully agree, for the time being we have build a scalable solution on AWS spot to server our docker builds. A detailed blog post en ref to the code https://040code.github.io/2020/05/25/scaling-selfhosted-action-runners

  5. jupe commented on Feb 16, 2021

    @jupe

    This is a big problem for us. We want to run gh runner in docker to easier scaling and isolation. But we need to also run services for tests. So our workaround for now is to run multiple runners on host, but scaling container with docker-compose is so much easier and convenient.

    Currently we have to create workarounds using non-optimal solutions to deploy tens of runners - or workarounds for container usage in jobs which is rather ugly. How to raise priority of this ?

    Just curious, how other's manage scaling the runners ? This is probably one of the most interested approach so far I've seen.. I guess many of us faced this same challenge when scaling gh-runners.. "Official" scaling proposals from GitHub would be more than welcome :) .

  6. vincentbrison commented on Feb 19, 2021

    @vincentbrison

    This is a big problem for us. We want to run gh runner in docker to easier scaling and isolation. But we need to also run services for tests. So our workaround for now is to run multiple runners on host, but scaling container with docker-compose is so much easier and convenient.

    Big kudos for @npalm and its solution on AWS. We also build a similar solution for GCP allowing us to scale our self hosted runners for a whole GitHub organization https://github.com/faberNovel/terraform-gcp-github-runner

  7. callum-tait-pbx commented on Apr 17, 2021

    @callum-tait-pbx

    @jupe we use https://github.com/summerwind/actions-runner-controller which has worked really well for us so far

  8. pratikbin commented on Jun 27, 2021

    @pratikbin

    waiting for this one so bad

  9. uwehdaub commented on Jul 2, 2021

    @uwehdaub

    @jpb Is there any possibility to get a higher priority on this?

  10. uwehdaub commented on Jul 8, 2021

    @uwehdaub

    For now we will use some workaround based on docker-compose.
    We have the following docker-compose.yaml file in the repo to setup the services.

    version: "3.3"
    services:
      nginx:
        image: nginx
      redis:
        image: redis
    

    We then connect the self-hosted runner which is also running inside docker with the created network.
    This is one example WF:

    name: Start docker compose
    on:
      workflow_dispatch:
    jobs:
      start-docker-compose:
        # should run as docker container with connection to the dockerd of the host
        runs-on: [self-hosted] 
        steps:
          - name: Checkout code
            uses: actions/checkout@v2
            with:
              fetch-depth: 1
          - name: Start docker compose
            id: start-docker-compose
            run: |
              project_prefix=my-project
              project_name="${project_prefix}-$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c 13 | tr '[:upper:]' '[:lower:]')"
              my_container_id=$(grep docker /proc/self/cgroup | head -n 1 | sed "s|^.*/docker/\(.*\)|\\1|")
    
              docker-compose -p "${project_name}" up -d
              while ! docker network inspect "${project_name}_default" > /dev/null ; do
                sleep 1
              done
    
              docker network connect "${project_name}_default" "${my_container_id}"
    
              echo "::set-output name=my_container_id::$my_container_id"
              echo "::set-output name=project_name::$project_name"
    
          - name: Check output
            run: |
              echo "Project name: ${{ steps.start-docker-compose.outputs.project_name}}"
              echo "Container ID: ${{ steps.start-docker-compose.outputs.remote_container_id}}"
    
          - name: Use the started docker compose services
            run: |
              # Install netcat to check redis
              apt-get update
              apt-get install -y netcat
              echo '--------------------'
              ping -c 1 nginx
              curl nginx
              echo '--------------------'
              ping -c 1 redis
              echo ping | netcat -w 2 redis 6379
          - name: Cleanup started docker compose services
            if: always()
            run: |
              docker network disconnect ${{ steps.start-docker-compose.outputs.project_name}}_default ${{ steps.start-docker-compose.outputs.my_container_id}}
              docker-compose -p ${{ steps.start-docker-compose.outputs.project_name}} down
    
  11. bryanmacfarlane commented on Jul 9, 2021

    @bryanmacfarlane
    Contributor

    @jpb , since you asked me, I'm ➕ on this, But adding @hross to weigh in since he's driving the runner area now. 🚀

  12. hross commented on Jul 12, 2021

    @hross
    Contributor

    We still want to do this and it's on our list but we don't have a date or schedule for shipping this type of feature right now.

  13. brandonschabell commented on Jul 13, 2021

    @brandonschabell

    Would love to see this prioritized. Can't really run docker-in-docker on Kubernetes self-hosted runners without this.

  14. 26 remaining items

  15. AllanOricil commented on May 8, 2023

    @AllanOricil

    @Sebastian-0 I understand that there are limitations. But the way this issue is currently written does not say what these limitations are exactly. The way it is currently written, specially its title, can lead people to believe that is not possible to run the runner inside a container in any ways, which, after some trials, I discovered it is not true.
    I know that there is a solution that allows the runner to run inside a docker container, because I was able to do it. However, this evidence alone does not prove that there isn't an edge case that won't work. So, that is why I'm asking an example of workflow that won't work with this solution. This can help other people to better understand what the real problem is.

  16. nbrugger-tgm commented on Jun 26, 2023

    @nbrugger-tgm

    @AllanOricil, I do not think that this issue can be closed as long as there is no official guide/info on this such as:

    • docker will not be supported
    • to run in docker you need to ...image... entry point ... network … mounts ... etc that is kept up to date by the gh-actions team
  17. timnolte commented on Aug 21, 2023

    @timnolte

    @Sebastian-0 I understand that there are limitations. But the way this issue is currently written does not say what these limitations are exactly. The way it is currently written, specially its title, can lead people to believe that is not possible to run the runner inside a container in any ways, which, after some trials, I discovered it is not true. I know that there is a solution that allows the runner to run inside a docker container, because I was able to do it. However, this evidence alone does not prove that there isn't an edge case that won't work. So, that is why I'm asking an example of workflow that won't work with this solution. This can help other people to better understand what the real problem is.

    @AllanOricil basically these items are not working: myoung34/docker-github-actions-runner#98

    Meaning that in GitHub Actions you can't really get container services working/running that might be needed. I was getting failures when attempting to use the "Local Registry" solution for GitHub Actions when building Docker images in order to test them.

    /usr/bin/docker create --name fc2ec27c43d44dc68880bee45a7b12fb_registry2_b6e2e5 --label c3f261 --network github_network_6886f8441d2f4f3489b19978db60872b --network-alias registry -p 5000:5000  -e GITHUB_ACTIONS=true -e CI=true registry:2
      83b0180533c3ec325b50cff90[42](https://github.com/ndigitals/ols-dockerfiles/actions/runs/5920922473/job/16052814082#step:2:45)8487490e74aaa907a46ac8ae45f08bc866755
      /usr/bin/docker start 83b0180533c3ec325b50cff90428487490e74aaa907a46ac8ae45f08bc866755
      Error response from daemon: network github_network_6886f8[44](https://github.com/ndigitals/ols-dockerfiles/actions/runs/5920922473/job/16052814082#step:2:47)1d2f4f3489b19978db60872b not found
      Error: failed to start containers: 83b0180533c3ec325b50cff90428487490e74aaa907a46ac8ae[45](https://github.com/ndigitals/ols-dockerfiles/actions/runs/5920922473/job/16052814082#step:2:48)f08bc866755
    

    I was trying to use the following Local Registry GitHub Actions setup for Docker image builds.

  18. AllanOricil commented on Aug 21, 2023

    @AllanOricil

    Interesting. Now it makes sense to me why this should not be closed. Thanks @timnolte

  19. fabio-s-franco commented on Sep 14, 2023

    @fabio-s-franco

    I have setup dind using terraform and a custom image based on dind. That all ran in my local WSL2, so I assume it wouldn't be far fetched to do it for the other scenarios. In an approach similar to what @AllanOricil, I did have to setup a bridge network properly, which took a lot of trial and error, but in the end it worked, both with a shared socket and with an independent socket. I will share here what I did as is and maybe someone can pick it up from it.

    This is not a solution, but whoever is having problems, may pickup some ideas from what I did:

    Terraform code

    Should be straightforward to convert it to anything else)

    Network

    resource "docker_network" "rke_network" {
    
      name = local.docker_network_name
    
      driver     = "bridge"
      attachable = true
      internal   = false
    
      ipam_config {
        ip_range = local.network_subnet
        subnet   = local.network_subnet
        gateway  = local.network_gateway
      }
    
      options = {
        "com.docker.network.bridge.enable_icc"           = "true"
        "com.docker.network.bridge.enable_ip_masquerade" = "true"
        "com.docker.network.bridge.host_binding_ipv4"    = "0.0.0.0"
        "com.docker.network.bridge.name"                 = local.iface_name
        "com.docker.network.driver.mtu"                  = "65000"
        "com.docker.network.driver.txqueuelen"           = "10000"
      }
    
      provisioner "local-exec" {
        command = "sudo ip link set dev eth0 txqueuelen 10000 && sudo ip link set dev eth0 mtu 65000 && sysctl -w net/netfilter/nf_conntrack_max=393216"
      }
    }

    Docker container node definition (where the agent would be)

    resource "docker_container" "node" {
      count = 4
    
      name  = "rke-dind-${local.nodes[count.index].ipv4_address}"
      image = "docker.local/dind-ssh:latest"
    
      privileged        = true
      publish_all_ports = true
    
      user          = "root"
      cgroupns_mode = "host"
      ipc_mode      = "shareable"
      stdin_open    = true
      tty           = true
      runtime       = "runc"
    
      network_mode = "bridge"
    
      env = [ "DOCKER_TLS_CERTDIR=\"\"", "AUTH_PUBKEY=${tls_private_key.ssh_key.public_key_openssh}", "AUTH_PRVKEY=${tls_private_key.ssh_key.private_key_openssh}" ]
    
      networks_advanced {
        name         = local.docker_network_name
        ipv4_address = local.nodes[count.index].ipv4_address
      }
    
      ports {
        internal = 22
        ip = "0.0.0.0"
        protocol = "tcp"
      }
    
      ports {
        internal = 2375
        ip = "0.0.0.0"
        protocol = "tcp"
      }
    
      ports {
        internal = 2376
        ip = "0.0.0.0"
        protocol = "tcp"
      }
    
      ports {
        internal = 2379
        ip = "0.0.0.0"
        protocol = "tcp"
      }
    }

    Building a custom dind image

    I did to enable ssh into it, but can also be used to add an agent for example.
    The RSA keys you should generate one yourself as they are used in the image build process (but could also be generated otherwise during build). My use case required that I had these keys pre-made elsewhere.

    Dockerfile

    FROM docker:dind AS dindssh
    
    # Install SSH server
    RUN apk add openssh-server
    
    # Generate SSH host keys
    RUN ssh-keygen -A
    
    # Copy sshd_config file
    COPY sshd_config /etc/ssh/sshd_config
    COPY daemon.json /etc/docker/daemon.json
    RUN chmod 700 /usr/local/bin/dockerd-entrypoint.sh
    COPY dockerd-entrypoint.sh /usr/local/bin/dockerd-entrypoint.sh
    RUN chmod +x /usr/local/bin/dockerd-entrypoint.sh
    
    # Add authorized keys for root user
    RUN mkdir -p /root/.ssh
    RUN touch /root/.ssh/authorized_keys
    RUN touch /root/.ssh/id_rsa
    RUN chmod 600 /root/.ssh/authorized_keys
    RUN chmod 600 /root/.ssh/id_rsa
    RUN chown root:root /root/.ssh/authorized_keys
    RUN chown root:root /root/.ssh/id_rsa
    
    # Expose SSH, Docker and etcd ports
    EXPOSE 22 2375 2379
    
    # Start SSH daemon and Docker daemon
    ENTRYPOINT (/usr/sbin/sshd -D &) && dockerd-entrypoint.sh
    CMD []

    Entrypoint file

    Don't remember where I got the base file from, but it is highly customized to work with SSH + DIND

    #!/bin/sh
    set -eu
    
    _tls_ensure_private() {
    	local f="$1"; shift
    	[ -s "$f" ] || openssl genrsa -out "$f" 4096
    }
    _tls_san() {
    	{
    		ip -oneline address | awk '{ gsub(/\/.+$/, "", $4); print "IP:" $4 }'
    		{
    			cat /etc/hostname
    			echo 'docker'
    			echo 'localhost'
    			hostname -f
    			hostname -s
    		} | sed 's/^/DNS:/'
    		[ -z "${DOCKER_TLS_SAN:-}" ] || echo "$DOCKER_TLS_SAN"
    	} | sort -u | xargs printf '%s,' | sed "s/,\$//"
    }
    _tls_generate_certs() {
    	local dir="$1"; shift
    
    	# if server/{ca,key,cert}.pem && !ca/key.pem, do NOTHING except verify (user likely managing CA themselves)
    	# if ca/key.pem || !ca/cert.pem, generate CA public if necessary
    	# if ca/key.pem, generate server public
    	# if ca/key.pem, generate client public
    	# (regenerating public certs every startup to account for SAN/IP changes and/or expiration)
    
    	if [ -s "$dir/server/ca.pem" ] && [ -s "$dir/server/cert.pem" ] && [ -s "$dir/server/key.pem" ] && [ ! -s "$dir/ca/key.pem" ]; then
    		openssl verify -CAfile "$dir/server/ca.pem" "$dir/server/cert.pem"
    		return 0
    	fi
    
    	# https://github.com/FiloSottile/mkcert/issues/174
    	local certValidDays='825'
    
    	if [ -s "$dir/ca/key.pem" ] || [ ! -s "$dir/ca/cert.pem" ]; then
    		# if we either have a CA private key or do *not* have a CA public key, then we should create/manage the CA
    		mkdir -p "$dir/ca"
    		_tls_ensure_private "$dir/ca/key.pem"
    		openssl req -new -key "$dir/ca/key.pem" \
    			-out "$dir/ca/cert.pem" \
    			-subj '/CN=docker:dind CA' -x509 -days "$certValidDays"
    	fi
    
    	if [ -s "$dir/ca/key.pem" ]; then
    		# if we have a CA private key, we should create/manage a server key
    		mkdir -p "$dir/server"
    		_tls_ensure_private "$dir/server/key.pem"
    		openssl req -new -key "$dir/server/key.pem" \
    			-out "$dir/server/csr.pem" \
    			-subj '/CN=docker:dind server'
    		cat > "$dir/server/openssl.cnf" <<-EOF
    			[ x509_exts ]
    			subjectAltName = $(_tls_san)
    		EOF
    		openssl x509 -req \
    				-in "$dir/server/csr.pem" \
    				-CA "$dir/ca/cert.pem" \
    				-CAkey "$dir/ca/key.pem" \
    				-CAcreateserial \
    				-out "$dir/server/cert.pem" \
    				-days "$certValidDays" \
    				-extfile "$dir/server/openssl.cnf" \
    				-extensions x509_exts
    		cp "$dir/ca/cert.pem" "$dir/server/ca.pem"
    		openssl verify -CAfile "$dir/server/ca.pem" "$dir/server/cert.pem"
    	fi
    
    	if [ -s "$dir/ca/key.pem" ]; then
    		# if we have a CA private key, we should create/manage a client key
    		mkdir -p "$dir/client"
    		_tls_ensure_private "$dir/client/key.pem"
    		chmod 0644 "$dir/client/key.pem" # openssl defaults to 0600 for the private key, but this one needs to be shared with arbitrary client contexts
    		openssl req -new \
    				-key "$dir/client/key.pem" \
    				-out "$dir/client/csr.pem" \
    				-subj '/CN=docker:dind client'
    		cat > "$dir/client/openssl.cnf" <<-'EOF'
    			[ x509_exts ]
    			extendedKeyUsage = clientAuth
    		EOF
    		openssl x509 -req \
    				-in "$dir/client/csr.pem" \
    				-CA "$dir/ca/cert.pem" \
    				-CAkey "$dir/ca/key.pem" \
    				-CAcreateserial \
    				-out "$dir/client/cert.pem" \
    				-days "$certValidDays" \
    				-extfile "$dir/client/openssl.cnf" \
    				-extensions x509_exts
    		cp "$dir/ca/cert.pem" "$dir/client/ca.pem"
    		openssl verify -CAfile "$dir/client/ca.pem" "$dir/client/cert.pem"
    	fi
    }
    
    export DOCKER_HOST=/var/run/docker.sock
    # no arguments passed
    # or first arg is `-f` or `--some-option`
    if [ "$#" -eq 0 ] || [ "${1#-}" != "$1" ]; then
    	# set "dockerSocket" to the default "--host" *unix socket* value (for both standard or rootless)
    	uid="$(id -u)"
    	if [ "$uid" = '0' ]; then
    		dockerSocket='unix:///var/run/docker.sock'
    	else
    		# if we're not root, we must be trying to run rootless
    		: "${XDG_RUNTIME_DIR:=/run/user/$uid}"
    		dockerSocket="unix://$XDG_RUNTIME_DIR/docker.sock"
    	fi
    	case "${DOCKER_HOST:-}" in
    		unix://*)
    			dockerSocket="$DOCKER_HOST"
    			;;
    	esac
    
    	# add our default arguments
    	if [ -n "${DOCKER_TLS_CERTDIR:-}" ]; then
    		_tls_generate_certs "$DOCKER_TLS_CERTDIR"
    		# generate certs and use TLS if requested/possible (default in 19.03+)
    		set -- dockerd \
    			--tlsverify \
    			--tlscacert "$DOCKER_TLS_CERTDIR/server/ca.pem" \
    			--tlscert "$DOCKER_TLS_CERTDIR/server/cert.pem" \
    			--tlskey "$DOCKER_TLS_CERTDIR/server/key.pem" \
    			"$@"
    		DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS="${DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS:-} -p 0.0.0.0:2376:2376/tcp"
    	else
    		# TLS disabled (-e DOCKER_TLS_CERTDIR='') or missing certs
    		set -- dockerd \
    			"$@"
    		DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS="${DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS:-} -p 0.0.0.0:2375:2375/tcp"
    	fi
    fi
    
    if [ "$1" = 'dockerd' ]; then
    	# explicitly remove Docker's default PID file to ensure that it can start properly if it was stopped uncleanly (and thus didn't clean up the PID file)
    	find /run /var/run -iname 'docker*.pid' -delete || :
    
    	if dockerd --version | grep -qF ' 20.10.'; then
    		# XXX inject "docker-init" (tini) as pid1 to workaround https://github.com/docker-library/docker/issues/318 (zombie container-shim processes)
    		set -- docker-init -- "$@"
    	fi
    
    	if ! iptables -nL > /dev/null 2>&1; then
    		# if iptables fails to run, chances are high the necessary kernel modules aren't loaded (perhaps the host is using nftables with the translating "iptables" wrappers, for example)
    		# https://github.com/docker-library/docker/issues/350
    		# https://github.com/moby/moby/issues/26824
    		modprobe ip_tables || :
    	fi
    
    	uid="$(id -u)"
    	if [ "$uid" != '0' ]; then
    		# if we're not root, we must be trying to run rootless
    		if ! command -v rootlesskit > /dev/null; then
    			echo >&2 "error: attempting to run rootless dockerd but missing 'rootlesskit' (perhaps the 'docker:dind-rootless' image variant is intended?)"
    			exit 1
    		fi
    		user="$(id -un 2>/dev/null || :)"
    		if ! grep -qE "^($uid${user:+|$user}):" /etc/subuid || ! grep -qE "^($uid${user:+|$user}):" /etc/subgid; then
    			echo >&2 "error: attempting to run rootless dockerd but missing necessary entries in /etc/subuid and/or /etc/subgid for $uid"
    			exit 1
    		fi
    		: "${XDG_RUNTIME_DIR:=/run/user/$uid}"
    		export XDG_RUNTIME_DIR
    		if ! mkdir -p "$XDG_RUNTIME_DIR" || [ ! -w "$XDG_RUNTIME_DIR" ] || ! mkdir -p "$HOME/.local/share/docker" || [ ! -w "$HOME/.local/share/docker" ]; then
    			echo >&2 "error: attempting to run rootless dockerd but need writable HOME ($HOME) and XDG_RUNTIME_DIR ($XDG_RUNTIME_DIR) for user $uid"
    			exit 1
    		fi
    		if [ -f /proc/sys/kernel/unprivileged_userns_clone ] && unprivClone="$(cat /proc/sys/kernel/unprivileged_userns_clone)" && [ "$unprivClone" != '1' ]; then
    			echo >&2 "error: attempting to run rootless dockerd but need 'kernel.unprivileged_userns_clone' (/proc/sys/kernel/unprivileged_userns_clone) set to 1"
    			exit 1
    		fi
    		if [ -f /proc/sys/user/max_user_namespaces ] && maxUserns="$(cat /proc/sys/user/max_user_namespaces)" && [ "$maxUserns" = '0' ]; then
    			echo >&2 "error: attempting to run rootless dockerd but need 'user.max_user_namespaces' (/proc/sys/user/max_user_namespaces) set to a sufficiently large value"
    			exit 1
    		fi
    		# TODO overlay support detection?
    		exec rootlesskit \
    			--net="${DOCKERD_ROOTLESS_ROOTLESSKIT_NET:-vpnkit}" \
    			--mtu="${DOCKERD_ROOTLESS_ROOTLESSKIT_MTU:-1500}" \
    			--disable-host-loopback \
    			--port-driver=builtin \
    			--copy-up=/etc \
    			--copy-up=/run \
    			${DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS:-} \
    			"$@"
    	elif [ -x '/usr/local/bin/dind' ]; then
    		# if we have the (mostly defunct now) Docker-in-Docker wrapper script, use it
    		set -- '/usr/local/bin/dind' "$@"
    	fi
    else
    	# if it isn't `dockerd` we're trying to run, pass it through `docker-entrypoint.sh` so it gets `DOCKER_HOST` set appropriately too
    	set -- docker-entrypoint.sh "$@"
    fi
    
    if [ "$AUTH_PUBKEY" ]; then
        echo "$AUTH_PUBKEY" > /root/.ssh/authorized_keys
        echo "$AUTH_PRVKEY" > /root/.ssh/id_rsa
    fi
    
    mount --make-shared / && mount --make-shared /sys && mount --make-shared /var/lib/docker
    exec "dockerd" "--tls=false"
    #exec "$@"

    daemon.json file

    {
        "debug": false,
        "hosts": [
            "tcp://0.0.0.0:2375",
            "unix:///var/run/docker.sock"
        ],
        "runtimes": {
            "sysbox-runc": {
                "path": "/usr/bin/sysbox-runc"
            }
        },
        "dns": ["1.1.1.1"],
        "userland-proxy": false
    }
  20. juannavalonribas commented on Feb 15, 2024

    @juannavalonribas

    Hello, any update about this feature?

  21. sergii-rybin-tfs commented on Aug 2, 2024

    @sergii-rybin-tfs

    Hello we need this feature also.

  22. ErcinDedeoglu commented on Jan 16, 2025

    @ErcinDedeoglu

    It seems we've waited long enough. What do you think? Are you looking forward to the demise of Docker?

  23. Ryangr0 commented on Feb 6, 2025

    @Ryangr0

    As a user of github actions together with https://github.com/myoung34/docker-github-actions-runner, it's completely unclear to me how this has still not received the attention that it needs. "Use AWS", or "Use k8s" is not a solution to the problem, or even a valid answer. Being able to run docker inside of your runner (which is going to be running in docker 99% of the time in modern settings) is a very basic feature imo. I'm frankly amazed I've had to spend days banging my head against the wall, only to find out it's not even supported. (Yet... Hopefully...) Because of volume mounts. Without any real explanation as to why things work this way.

  24. gavinltomra commented on Feb 14, 2025

    @gavinltomra

    So, reading this comment near the error message:

    // Check whether we are inside a container.
    // Our container feature requires to map working directory from host to the container.
    // If we are already inside a container, we will not able to find out the real working direcotry path on the host.

    I assume that the main problem with the docker-in-docker scenario is that volume mounts have to be specified based on host paths rather than container-internal paths (i.e. where the runner is running)?

    If so, shouldn't it be possible to allow this by performing that translation? You can require certain things from the host container (such as mounting the docker.sock and a suitable path to use for the work and tmp directories, which is all that appears to be needed), and you can translate back to host paths by inspecting your own container's mounts (given that the docker.sock was mounted). Since you can parse the mounts you can even error out with a helpful message if you need a path to be mounted from the host and it wasn't.

    You can get the mounts of your own container with docker inspect -f '{{json .Mounts}}' $(cut -d/ -f3 < /proc/1/cpuset).

  25. alexander-heimbuch commented on Mar 5, 2025

    @alexander-heimbuch

    I've been watching this issue for almost 6 months now and finally managed to implement the actions runner myself based on the runner-container-hooks: https://github.com/alexander-heimbuch/github-runner-container-hooks

    Maybe this helps someone else too or Github Actions could integrate that in their own container runner.

  26. boldandbusted commented on Jun 13, 2025

    @boldandbusted

    I'd love to see this also. There's amazing resource conservation, efficiency and cost savings to being able to have containerized ephemeral Runners, and running Docker-out-of-Docker seems like a legitimate approach. There are a large number of GitHub Actions (especially on the Marketplace) that are delivered as containers, and cannot run presently in a containerized Runner. Thanks.

  27. besmirzanaj commented on Jun 26, 2025

    @besmirzanaj

    This is such a core feature that other source code management organizations have implemented in their early days, eg. gitlab. Not sure why no effort is being put here since there's a lot of paid customer asking for this

  28. Darth-Jurassic commented on Jul 6, 2025

    @Darth-Jurassic

    Up. I was same really surprised by this feature still not implemented. Moreover, I have installed the docker CLI and added the socket, but still getting the same error. Is it possible to remove this check at least?

  29. jk89 commented on Nov 1, 2025

    @jk89

    I have a partial solution using Sysbox: https://github.com/jk89/yolo-runner

    • Runner works inside a container.
    • Nested containers (jobs/services) run, enabling Docker-based Actions and DiD³ scenarios.
    • Host volume mounts (_work, _tools) work.

    Network sharing between multiple runners isn’t handled.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions