Skip to main content
GitHub Actions runners are in private preview. Contact support to request access.
This page covers jobs that run on sandboxes (type=sandbox). It explains where a job’s AWS credentials come from, what can and can’t reach them, and how to get credentials into Docker containers safely.

How sandbox jobs get AWS credentials

Each job runs in its own Firecracker microVM. For each job, Ravion creates an IAM role in your execution environment’s AWS account. The role’s name starts with rvn-ci-, and its path is /rvn-ci/. The sandbox host assumes that role with AWS STS and serves the credentials inside the microVM at the standard instance metadata endpoint, 169.254.169.254. The paths match EC2’s instance metadata service (IMDS). Each STS session lasts 15 minutes, and Ravion refreshes it automatically for as long as the job runs. The AWS CLI and AWS SDKs running directly on the runner find these credentials with no setup:
Keep these in mind:
  • IMDSv2 only. Requests must first get a session token with PUT /latest/api/token. IMDSv1 requests, which send no token, get 401 Unauthorized. Current AWS CLI and SDK versions use IMDSv2 automatically.
  • No region. The endpoint doesn’t serve the instance’s placement or region. Set AWS_REGION and AWS_DEFAULT_REGION yourself, as in the example above.
  • No policies by default. The job role has no IAM policies attached by default. Calls such as aws sts get-caller-identity work, but calls that need permissions return AccessDenied. For jobs that need AWS access, use GitHub OIDC.

Docker containers can’t reach the endpoint

Docker containers in the job can’t reach 169.254.169.254. That includes:
  • services: containers;
  • container: jobs;
  • docker run with the default network;
  • docker compose stacks;
  • any other Docker bridge network.
Requests from these containers don’t fail with an error. They time out. Firecracker’s metadata service sends every reply with an IP time to live (TTL) of 1, and the hop from the microVM’s network interface to the Docker bridge drops it. Unlike the EC2 hop limit, this isn’t configurable. Containers started with docker run --network host share the microVM’s network, so they do reach the endpoint. AWS SDKs inside containers probe the endpoint when they look for credentials, and wait for that probe to time out. For example, the AWS SDK for Ruby probes synchronously, with timeouts of about 1 second to open the connection and 1 second to read it. That can make an application slow to start, or make its first AWS call hang.

Get credentials into containers

Stop SDKs from probing the endpoint

In every container that can’t reach the endpoint, set AWS_EC2_METADATA_DISABLED=true. SDKs then skip the metadata endpoint instead of waiting for it to time out. Use this together with one of the options below to give the container credentials.

Run services with Docker Compose

services: containers start before any step runs, so a step can’t hand them credentials. Instead, start the containers with docker compose in a step, and pass the job’s credentials in an env file:
compose.yaml
The keys never appear in the workflow file or the job log. Don’t run docker compose config or print the env file, because both show the values.
Exported credentials don’t refresh inside the container. They expire within 15 minutes of export, and possibly sooner, because Ravion issues a new session before the old one expires. AWS_CREDENTIAL_EXPIRATION in the env file shows when. For containers that run longer, export again and restart them, or use GitHub OIDC with a longer session.
Write the file under $RUNNER_TEMP and remove it when the job ends, as above, so it isn’t kept in the job’s disk cache.

Use the host network for one-off containers

A docker run container on the host network reaches the endpoint directly, and SDKs inside it refresh credentials as usual. This example passes AWS_REGION from the job’s env:
The container shares the microVM’s network, so its ports are the runner’s ports and it can reach anything the runner can.

Use GitHub OIDC

For roles that depend on the branch, environment or repository, use GitHub’s OpenID Connect provider with aws-actions/configure-aws-credentials. This works on Ravion runners the same way as on GitHub-hosted runners, and lets you attach exactly the policies the job needs.
The action exports the credentials as environment variables for later steps. Those take precedence over the job role from the metadata endpoint. The action also masks the values in the log. To forward them to a container, pass each variable’s name only (-e AWS_ACCESS_KEY_ID), or write them to an env file for docker compose as in Run services with Docker Compose. In a container: job, steps already run inside the container, so the action’s credentials reach later steps there directly.

Keep keys out of the job log

Values you write into services.*.env, container.env or a docker run -e NAME=value argument can show up in the job log. Masking only covers values GitHub knows are secret.
  • Pass credentials with env files, or with -e NAME without a value.
  • Reference credentials stored as GitHub secrets with ${{ secrets.NAME }}. GitHub masks them.
  • Mask any other value before anything can print it:

Compared with other runners

If you’re moving from RunsOn, workflows whose services: or container: code relied on the instance role need one of the options above. AWS calls made directly in steps find the job role instead of the instance role. The job role has no policies by default, so give those steps the permissions they need with GitHub OIDC.

Next steps

Runners

Labels, sizing and requirements.

Sandboxes

What runs inside a sandbox.