GitHub Runner Home Directory, and Why It Is Not the Working Directory
On a GitHub-hosted Linux runner, HOME is /home/runner and your checked-out code is not in it. Almost every confusing path problem in CI comes from assuming those are the same directory.
A step that writes to ~/.npmrc and a step that writes to ./.npmrc are writing to two different places on a runner, three directories apart. Locally they often amount to the same thing because you work inside your own home directory.
These are the paths as they actually are, plus the two cases that surprise people: container jobs, where HOME moves, and self-hosted runners, where it depends on who installed the service.
Paths on a hosted Linux runner
| What | Path | Environment variable |
|---|---|---|
| Home directory | /home/runner | HOME |
| Your checked-out code | /home/runner/work/<repo>/<repo> | GITHUB_WORKSPACE |
| Work root for all repos | /home/runner/work | RUNNER_WORKSPACE is one level in |
| Scratch space for the run | /home/runner/work/_temp | RUNNER_TEMP |
| Preinstalled tool versions | /opt/hostedtoolcache | RUNNER_TOOL_CACHE |
Other hosted operating systems
| Runner | Home | Tool cache |
|---|---|---|
| Ubuntu | /home/runner | /opt/hostedtoolcache |
| macOS | /Users/runner | /Users/runner/hostedtoolcache |
| Windows | C:\Users\runneradmin | C:\hostedtoolcache\windows |
Container jobs move HOME
When a job specifies a container:, steps do not run as the runner user in the runner filesystem. HOME is set to /github/home, which is a bind mount of /home/runner/work/_temp/_github_home from the host, and the workspace is mounted at /__w/<repo>/<repo>. Anything an action or tool writes to ~ therefore lands in that mounted directory, not in /home/runner and not in your repository. It does survive between steps of the same job, because the mount is shared, and it does not survive the job.
Self-hosted and managed runners
- Self-hosted: HOME belongs to the operating-system user running the runner service, so it is whatever that account is. The work directory is
_workinside the runner installation directory rather than under/home/runner. - Because self-hosted runners are reused between jobs, whatever a job leaves in HOME is visible to the next one. That is occasionally a useful cache and always a security consideration, since one repository can read what another left behind.
- Latchkey runners are ephemeral and Ubuntu-based, so the hosted Linux paths above apply and nothing persists in HOME between jobs by construction.
Where this bites
- Cache actions that key on a path under
~while the tool actually writes inside the workspace, or the reverse. Print the path in a step before caching it. - Credentials helpers.
docker login,gh authandgit config --globalall write to HOME, so on a self-hosted runner they outlive the job. - Scripts that use
~and get run inside a container step, where~is/github/homeand not the directory the earlier steps wrote to. - Disk space. The workspace and HOME share the same volume on hosted runners, so a large dependency cache in HOME reduces the room available for a build.
Frequently asked questions
What is the home directory on a GitHub Actions runner?
/home/runner, on macOS /Users/runner, and on Windows C:\Users\runneradmin. In a container job HOME is /github/home instead. Read the HOME environment variable rather than assuming, since a container step changes it.Is the home directory the same as the workspace?
/home/runner and your checked-out repository is at /home/runner/work/<repo>/<repo>, which is what GITHUB_WORKSPACE points to and where every run step starts. They are on the same disk and are not the same directory.Does anything written to the home directory persist between jobs?
git config --global or docker login remain available to whatever runs next.How do I find the home directory during a run?
run: echo "$HOME", alongside echo "$GITHUB_WORKSPACE" and pwd. On a job you suspect of a path problem this is faster than reasoning about it, and it is the only reliable answer inside a container.