How to check if a process is running inside docker container?

95,365

Solution 1

To check inside a Docker container if you are inside a Docker container or not can be done via /proc/1/cgroup. As this post suggests you can to the following:

Outside a docker container all entries in /proc/1/cgroup end on / as you can see here:

vagrant@ubuntu-13:~$ cat /proc/1/cgroup
11:name=systemd:/
10:hugetlb:/
9:perf_event:/
8:blkio:/
7:freezer:/
6:devices:/
5:memory:/
4:cpuacct:/
3:cpu:/
2:cpuset:/

Inside a Docker container some of the control groups will belong to Docker (or LXC):

vagrant@ubuntu-13:~$ docker run busybox cat /proc/1/cgroup
11:name=systemd:/
10:hugetlb:/
9:perf_event:/
8:blkio:/
7:freezer:/
6:devices:/docker/3601745b3bd54d9780436faa5f0e4f72bb46231663bb99a6bb892764917832c2
5:memory:/
4:cpuacct:/
3:cpu:/docker/3601745b3bd54d9780436faa5f0e4f72bb46231663bb99a6bb892764917832c2
2:cpuset:/

Solution 2

Docker creates .dockerenv and .dockerinit (removed in v1.11) files at the top of the container's directory tree so you might want to check if those exist.

Something like this should work.

#!/bin/bash
if [ -f /.dockerenv ]; then
    echo "I'm inside matrix ;(";
else
    echo "I'm living in real world!";
fi

Solution 3

We use the proc's sched (/proc/$PID/sched) to extract the PID of the process. The process's PID inside the container will differ then it's PID on the host (a non-container system).

For example, the output of /proc/1/sched on a container will return:

root@33044d65037c:~# cat /proc/1/sched | head -n 1
bash (5276, #threads: 1)

While on a non-container host:

$ cat /proc/1/sched  | head -n 1
init (1, #threads: 1)

This helps to differentiate if you are in a container or not. eg you can do:

if [[ ! $(cat /proc/1/sched | head -n 1 | grep init) ]]; then {
    echo in docker
} else {
    echo not in docker
} fi

Solution 4

Thomas' solution as code:

running_in_docker() {
  (awk -F/ '$2 == "docker"' /proc/self/cgroup | read non_empty_input)
}

Note

The read with a dummy variable is a simple idiom for Does this produce any output?. It's a compact method for turning a possibly verbose grep or awk into a test of a pattern.

Additional note on read

Solution 5

Using Environment Variables

For my money, I prefer to set an environment variable inside the docker image that can then be detected by the application.

For example, this is the start of a demo Dockerfile config:

FROM node:12.20.1 as base
ENV DOCKER_RUNNING=true
RUN yarn install --production
RUN yarn build

The second line sets an envar called DOCKER_RUNNING that is then easy to detect. The issue with this is that in a multi-stage build, you will have to repeat the ENV line every time you FROM off of an external image. For example, you can see that I FROM off of node:12.20.1, which includes a lot of extra stuff (git, for example). Later on in my Dockerfile I then COPY things over to a new image based on node:12.20.1-slim, which is much smaller:

FROM node:12.20.1-slim as server
ENV DOCKER_RUNNING=true
EXPOSE 3000
COPY --from=base /build /build
CMD ["node", "server.js"]

Even though this image target server is in the same Dockerfile, it requires the ENV var to be defined again because it has a different base image.

If you make use of Docker-Compose, you could instead easily define an envar there. For example, your docker-compose.yml file could look like this:

version: "3.8"
services:
  nodeserver:
    image: michaeloryl/stackdemo
    environment:
      - NODE_ENV=production
      - DOCKER_RUNNING=true
Share:
95,365

Related videos on Youtube

harryz
Author by

harryz

never ask dumb question

Updated on April 27, 2022

Comments

  • harryz
    harryz about 2 years

    [Updated1] I have a shell which will change TCP kernel parameters in some functions, but now I need to make this shell run in Docker container, that means, the shell need to know it is running inside a container and stop configuring the kernel.

    Now I'm not sure how to achieve that, here is the contents of /proc/self/cgroup inside the container:

    9:hugetlb:/
    8:perf_event:/
    7:blkio:/
    6:freezer:/
    5:devices:/
    4:memory:/
    3:cpuacct:/
    2:cpu:/docker/25ef774c390558ad8c4e9a8590b6a1956231aae404d6a7aba4dde320ff569b8b
    1:cpuset:/
    

    Any flags above can I use to figure out if this process is running inside a container?

    [Updated2]: I have also noticed Determining if a process runs inside lxc/Docker, but it seems not working in this case, the content in /proc/1/cgroup of my container is:

    8:perf_event:/
    7:blkio:/
    6:freezer:/
    5:devices:/
    4:memory:/
    3:cpuacct:/
    2:cpu:/docker/25ef774c390558ad8c4e9a8590b6a1956231aae404d6a7aba4dde320ff569b8b
    1:cpuset:/
    

    No /lxc/containerid

    • Henk Langeveld
      Henk Langeveld about 10 years
      Not a very clear question. Why do you need this?
    • Johannes 'fish' Ziemke
      Johannes 'fish' Ziemke about 10 years
    • harryz
      harryz about 10 years
      @fish no /lxc/<containerid> in my case, see update
    • harryz
      harryz about 10 years
      @HenkLangeveld kernel parameters is read-only in Docker container,so I need to know if my shell is running inside containers and disable kernel functions in my shell. see update.
    • Henk Langeveld
      Henk Langeveld about 10 years
      Some steps in the script try to modify kernel parameters, and need to be skipped when running in Docker. Clear.
  • larsks
    larsks about 9 years
    Except...this will fail in some envrionments, because, e.g., 3:cpu,cpuacct:/system.slice/docker-1ce79a0dec4a2084d54acf187‌​a1e177e0339dc90d0218‌​b48b4456576ecaf291e.‌​scope would not match. Simpler to grep -q docker /proc/1/cgroup; the result code from that should also be sufficient.
  • Daniel Alder
    Daniel Alder over 8 years
    read might work for bash, but in the most used dash shell you have to use either read dummy (or similar) or use a construct like [ -n "$(command)" ]
  • Henk Langeveld
    Henk Langeveld over 8 years
    @DanielAlder Good catch, Daniel. I will update the text.
  • Henk Langeveld
    Henk Langeveld over 8 years
    Previously this claimed that any Bourne compatible shell supports the plain read without variable name. This is only true for bash and ksh93. The Opengroup only specifies read var and does not mention read behaviour without at least one variable. In bash and ksh93, if no var is given, read uses the shell variable REPLY.
  • sosiouxme
    sosiouxme over 8 years
    Unless, of course, you or someone else has created /.dockerinit on your host (perhaps by accident), in which case it will be wrong outside a container.
  • automationleg
    automationleg over 8 years
    If somebody else made it in / then they are root and you've got worse problems than knowing whether you are in docker or no.
  • Paul
    Paul almost 8 years
    @HenkLangeveld This would fix your solution: (awk -F: '$3 ~ /docker/' /proc/self/cgroup | read non_empty_input)
  • Fabian Lange
    Fabian Lange almost 8 years
    this is actually quite a valuable information. thanks
  • ReactiveRaven
    ReactiveRaven over 7 years
    Beware relying on /.dockerenv in the long term. It is not intended to be used this way.
  • BrianV
    BrianV almost 7 years
    Depending on the OS, "init" might need to be replaced with "systemd". More information on systemd here.
  • Shubham Chaudhary
    Shubham Chaudhary almost 7 years
    As mentioned by @BrianV, this doesn't work for me too.
  • Shubham Chaudhary
    Shubham Chaudhary almost 7 years
    Why can't we just use awk -F: '$3 ~ /docker/' /proc/self/cgroup | read? Works for me.
  • Stefan Majewsky
    Stefan Majewsky over 6 years
    In a Docker container running on a k8s cluster, head -n1 /proc/1/sched returns dumb-init (1, #threads: 1), so the check suggested in this answer fails. (Also, contrary to what the answer suggests, the PID is shown as "1" in that line although I'm doing this in a container.)
  • Stefan Majewsky
    Stefan Majewsky over 6 years
    Does not work for me. From within a k8s-scheduled Docker container, readlink /proc/self/ns/pid and readlink /proc/1/ns/pid produce the same output.
  • Greg Bray
    Greg Bray over 6 years
    @StefanMajewsky Might want to try using github.com/jessfraz/amicontained to see what features are enabled in the container runtime.
  • samfr
    samfr about 6 years
    it is not strictly true that "Outside a docker container all entries in /proc/1/cgroup end on /". On ubuntu 16.04 for example I have: 12:perf_event:/ 11:blkio:/init.scope 10:cpuset:/ 9:devices:/init.scope 8:hugetlb:/ 7:cpu,cpuacct:/init.scope 6:net_cls,net_prio:/ 5:memory:/init.scope 4:pids:/init.scope 3:rdma:/ 2:freezer:/ 1:name=systemd:/init.scope
  • Christian
    Christian over 4 years
    This pretty much only works on Linux, not on Darwin or other BSDs that don't even use a procfs.
  • Robert Lacroix
    Robert Lacroix over 4 years
    @Christian Docker/LXC are Linux only things, so that's fine, right :)?
  • Christian
    Christian over 4 years
    @RobertLacroix so you're saying if you don't find a procfs, you're not in Docker? Well, that's fair enough I guess...
  • Beni Cherniavsky-Paskin
    Beni Cherniavsky-Paskin about 4 years
    fwiw, Podman does not create /.dockerenv. It does create /run/.containerenv but by similar logic, sounds like implementation detail not to be relied upon. See github.com/containers/libpod/issues/3586 for some podman-specific alternatives.
  • frakman1
    frakman1 about 4 years
    @larsks Indeed, grep is simpler. However, using -q didn't work on my docker container (command returned nothing) so it's better to omit it.
  • larsks
    larsks about 4 years
    @frakman1 the grep -q command is not expected to return anything. See the grep(1) man page.
  • jpkotta
    jpkotta about 4 years
    This is definitely not a universal solution. You can (sort of) use whatever you want for a container's PID 1. E.g. if you do docker run --init ... it will be docker-init. If you do e.g. docker run ... head -n 1 /proc/1/sched it will be head.
  • truthadjustr
    truthadjustr almost 4 years
    This method is not durable.
  • bo0k
    bo0k almost 4 years
    in MSYS2 ls -ali / | sed '2!d' |awk {'print $1'} 232779805740174872
  • yurenchen
    yurenchen over 3 years
    same as ls -di / ? seems inode num not reliable on different platform
  • Michael Altfield
    Michael Altfield over 3 years
    this is the only thing that worked for me to differentiate between a Xen domU host and its docker container
  • Nick Ribal
    Nick Ribal over 3 years
    Here's a shorter version and without violating any shellcheck rules: EXECUTABLE_NAME=$(if head -n 1 /proc/1/sched | grep -q 'tini'; then echo "docker run [name of this container]"; else echo "${0}"; fi). This assumes that you're using tini in your container, of course.
  • Kokos
    Kokos about 3 years
    This answer is the only one that still works for Docker v20. Simplest code to wrap it (exit code 0 if running with different init than systemd or init: awk '{exit ($1 ~ /^init|systemd$/)}' /proc/1/sched
  • Eric
    Eric almost 3 years
    care to explain what the code does? Thank you.
  • four43
    four43 almost 3 years
    This is great, just being explicit
  • Neil Mayhew
    Neil Mayhew almost 3 years
    stat -c %i is simpler than ls -ali / | sed '2!d' |awk {'print $1'}
  • Prosunjit Biswas
    Prosunjit Biswas about 2 years
    just an alternative way of writing the above test test -f /.dockerenv && echo inside docker || echo not inside docker
  • Wolfram Rösler
    Wolfram Rösler about 2 years
    Instead of cat /proc/1/sched | head -n 1 you can simply use head -n 1 /proc/1/sched.
  • Wolfram Rösler
    Wolfram Rösler about 2 years
    /proc/1/cgroup exists in Linux hosts as well, not only in containers
  • cowlinator
    cowlinator almost 2 years
    Does this work in windows container?
  • Ott Toomet
    Ott Toomet almost 2 years
    Beware that some programs do purge environment when starting (e.g shiny server). The variables are there if you open a shell, but empty within the program. In that case check out how to configure those vars for that particular software.