245

Is there any way to determine if a process (script) runs inside an lxc container (~ Docker runtime)? I know that some programs are able to detect whether they run inside a virtual machine, is something similar available for lxc/docker?

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
Mate Varga
  • 3,144
  • 2
  • 14
  • 17
  • It might seem pedantic, but it would be best to rephrase your question to describe a problem you're having and ask how to solve it -- without that, the question stands a higher chance of being closed. In many cases it's difficult to make that change but in yours it wouldn't be hard to simply rephrase if you wish. – mah Nov 15 '13 at 21:04
  • there is an interesting response when issuing this command while inside a container : uptime – Scott Stensland Feb 19 '17 at 01:16

19 Answers19

239

Docker creates a .dockerenv file at the root of the directory tree inside container. This can be seen by performing an ls -la /.dockerenv to show that it is created on container startup.

You can run this script to verify:

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

MORE: Ubuntu actually has a bash script: /bin/running-in-container and it can return the type of container it has been invoked in. Might be helpful. Don't know about other major distros though.

Archimedes Trajano
  • 35,625
  • 19
  • 175
  • 265
at0S
  • 4,522
  • 2
  • 14
  • 10
205

The most reliable way is to check /proc/1/cgroup. It will tell you the control groups of the init process, and when you are not in a container, that will be / for all hierarchies. When you are inside a container, you will see the name of the anchor point. With LXC/Docker containers, it will be something like /lxc/<containerid> or /docker/<containerid> respectively.

tshepang
  • 12,111
  • 21
  • 91
  • 136
jpetazzo
  • 14,874
  • 3
  • 43
  • 45
  • 14
    docker now uses `docker` instead of `lxc` in those paths – Andy Nov 21 '15 at 16:29
  • 6
    Does not work for lxd/lxc containers, but http://stackoverflow.com/a/20010626/170230 does. – Draco Ater Jun 08 '16 at 06:39
  • 2
    With later versions of systemd it looks like you can't rely on process 1 using `/` for all cgroups; on my Debian 9 system (systemd 232) only three of the ten cgroups (`3:cpuset`, `4:perf_event` and `7:freezer`) are at root; the rest are under `/init.scope`. That said, I think that searching that file for `:/docker/` is probably the most reliable heuristic at the moment. – cjs Feb 28 '18 at 07:40
  • 3
    `grep 'docker\|lxc' /proc/1/cgroup` works for me on Docker 18.09. – rypel Feb 01 '19 at 11:04
  • Here's a tighter (extended) regex that assumes a `sha256` hash in the `basename` position: `grep -Eq '/(lxc|docker)/[[:xdigit:]]{64}' /proc/1/cgroup` – rubicks Jun 14 '19 at 14:40
  • 1
    Combining two answers for increased robustness: `if [[ -f /.dockerenv ]] || grep -Eq '(lxc|docker)' /proc/1/cgroup; then echo True; else echo False; fi` (Though I don't know if cgroup file is in every linux; You might whant to add extra checks) – Boop Jul 02 '19 at 14:25
  • 4
    Not working for me. Host Ubuntu 19.04, guest Ubuntu 18.04 using LXC privileged container. /proc/1/cgroup does NOT contain the lxc string. – Gab Sep 01 '19 at 11:51
  • Not sure this is correct when using a schroot instead, in such case the cgroup is still the same – Treviño Jan 20 '21 at 13:22
  • Worth adding that the same way wouldn't work for detecting `chroot`, because if `/proc/` is mounted inside chroot the `cgroup` gonna have same content. There's a different way with `/proc`, but apparently no unified way to detect both chroot and docker at the same time. – Hi-Angel Aug 02 '21 at 13:54
  • 10
    On my docker image: cat /proc/1/cgroup 0::/ so doesn't work. – Brett Sutton Nov 18 '21 at 10:54
  • 1
    I'm running fedora 34 and the output of /proc/1/cgroup inside a container ir 0::/ If the linux distribuition relies on systemd a possible way is to check if the contents of /proc/1/comm are not 'systemd' – Henrique Capozzi Dec 11 '21 at 15:51
  • test it with alpine and it does not work... – Gabriel Jun 14 '23 at 16:18
  • As [stated here](https://www.baeldung.com/linux/is-process-running-inside-container), you might also want to try: `cat /proc/self/mountinfo` If it works, that’s because it’s using control group v2, instead of v1 (which uses /proc/1/cgroup). – Andorkan Jul 15 '23 at 17:47
29

On a new ubuntu 16.04 system, new systemd & lxc 2.0

sudo grep -qa container=lxc /proc/1/environ
larss
  • 291
  • 3
  • 2
  • 2
    This works for me on Ubuntu focal 20.04. None of the answers above this point did. – Jonathan Hartley Apr 23 '20 at 15:18
  • Thanks! it works for lxc! Can you please explain why '-a' is needed? Isn't `grep -q container=lxc /proc/1/environ` enough? – Alek Apr 28 '21 at 16:55
  • 1
    `/proc/$$/environ` separates environment variables with null bytes. Without `-a`, this passage from the man page applies: > By default, TYPE is binary, and grep suppresses output after null input binary data is discovered – Adam Azarchs Sep 22 '21 at 20:56
  • For me, using podman, `/proc/1/environ` contained `container=podman` instead. So I used `grep -qa '^container='` instead. – fstamour Oct 28 '22 at 18:14
20

A concise way to check for docker/lxc in a bash script is:

#!/bin/bash
if grep -sq 'docker\|lxc' /proc/1/cgroup; then
   echo "I am running on Docker."
fi
Ömer An
  • 600
  • 5
  • 16
oNaiPs
  • 551
  • 5
  • 14
17

Handy Python function to check if running in Docker:

def in_docker():
    """ Returns: True if running in a Docker container, else False """
    with open('/proc/1/cgroup', 'rt') as ifh:
        return 'docker' in ifh.read()
tshepang
  • 12,111
  • 21
  • 91
  • 136
JJC
  • 9,547
  • 8
  • 48
  • 53
  • 5
    Important Note! This does not appear to work when the container is running in kubernetes. Instead, replace the last line with 'kubepod' in place of 'docker'. (Or, put in an "or" statement that checks for both ;)) – JJC Jan 13 '19 at 03:20
  • 2
    It's `kubepods` I guess. – rookie099 Aug 29 '19 at 07:53
12

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.

Founder
  • 541
  • 5
  • 6
  • Depending on the OS, "init" might need to be replaced by "systemd". More information on systemd [here](https://www.tecmint.com/systemd-replaces-init-in-linux/). – BrianV Jun 23 '17 at 01:50
  • Yes, but the point wasn't the name of the init process, the point was the process number. – MillerGeek May 02 '18 at 14:31
  • 1
    This seems to only work on Docker. In an LXC container It's returning Systemd PID 1 – MillerGeek May 02 '18 at 14:32
  • 2
    It's now returning 1 in docker as well. It is usually `sh` and not `init` there, but it may be almost anything in either. – Jan Hudec May 06 '20 at 14:58
  • 3
    Under docker, this is no longer the case - ```bash-5.0# cat /proc/1/sched bash (1, #threads: 1)``` – shalomb Jun 04 '20 at 10:57
10

As of 2022, with lxd v4.0+, none of the answers so far work for both docker and lxc.

  • A .dockerenv file doesn't work for non-docker containers.
  • Checking that all hierarchies in /proc/1/cgroup are / kinda maybe works. However, some hierarchies on non-containers are /init.scope (Ubuntu 20.04 cgroup 0 and 1). So also not entirely reliable.
  • Checking for container=lxc in /proc/1/environ works for lxc but not docker. Also, it requires root rights.

The only way I've found so far that works reliably on both CentOS and Ubuntu with lxc (4.0) containers and Docker, and also doesn't require root rights, is to check PID 2.

On all host systems, PID 2 is kthreadd:

$ ps -p 2
  PID TTY          TIME CMD
    2 ?        00:00:00 kthreadd

In containers, this PID either doesn't exist, or isn't kthreadd. Both docker and lxc show:

root@85396f8bce58:/# ps -p 2
    PID TTY          TIME CMD
root@85396f8bce58:/# 

The best way seems to be to check /proc/2/status:

$ head -n1 /proc/2/status
Name:   kthreadd

So something like this seems to work:

if [ -n "$(grep 'kthreadd' /proc/2/status 2>/dev/null)" ]; then
    echo "Not in container"
else
    echo "In container";
fi
Ferry Boender
  • 668
  • 1
  • 7
  • 14
8

The easiest way would be to check the environment. If you have the container=lxc variable, you are within a container.

Otherwise, if you are root, you can try to perform mknod or mount operation, if it fails, you are most likely in a container with dropped capabilities.

creack
  • 116,210
  • 12
  • 97
  • 73
  • This one works not only for docker (I didn't check that), but more importantly for lxd/lxc containers (checked), where `/proc/1/cgroup` does not allow you to detect that. – Draco Ater Jun 08 '16 at 06:37
  • 5
    can you edit the answer with code instead of pseudocode? "container=lxc" ?is not proper anything. do you mean something like if [[ "lxc" = "$container" ]] ? – Alexander Mills Dec 20 '16 at 10:20
  • 4
    I mean...it is weird, usually env variables are in all caps, so looking for some precision here – Alexander Mills Dec 20 '16 at 10:22
  • 13
    `docker run alpine env` does not give anything that looks like that variable – Archimedes Trajano Mar 10 '19 at 16:01
5

This is an old question, but a REALLY good one. :)

I've written some automation scripts that we run on baremetal, VM and in a docker container, with logic branching based on which platform the script is executing on. In my case I have the privilege of creating both the container and the docker image, so this solution will only work if you are in control of the entire stack:

Snippet of Dockerfile:

FROM ubuntu:18.04

ENV PLATFORM="docker"

RUN apt update; \
...

The script can then just check the value of $PLATFORM for desired outcomes on each platform:

#!/bin/bash

# Check for executor specification in environment
case $PLATFORM in
  docker)
    # If running in Docker, do this stuff
    echo "Running containerized, proceeding..."
    ;;
  virtual)
    # If running in a VM, do different stuff
    echo "Running on a VM, loading VM stuff..."
    modprobe some-kernel-module
    ;;
  *)
    echo "Unknown executor specified! Exiting..."
    exit 1
    ;;
esac

I've omitted baremetal in the above code to keep it concise.

mainmachine
  • 150
  • 2
  • 7
3

This SO Q&A: "Find out if the OS is running in a virtual environment"; though not the same as the OP's question, it does indeed answer common cases of finding which container you're in (if at all).

In particular, install and read the code of this bash script which seems to work pretty well:

virt-what :

sudo apt install virt-what
Community
  • 1
  • 1
kaiwan
  • 2,114
  • 1
  • 18
  • 23
3

Check for all the solutions above in Python:

import os

def in_container():
    proc_1 = r'/proc/1/sched'

    if os.path.exists(proc_1):
        with open(proc_1, 'r') as fp:
            out = fp.read()
    else:
        out = ''

    checks = [
        'docker' in out,
        '/lxc/' in out,
        out.split(' ')[0] not in ('systemd', 'init',),
        os.path.exists('./dockerenv'),
        os.path.exists('/.dockerenv'),
        os.path.exists('/.dockerinit'),
        os.getenv('container') is not None
    ]
    return any(checks)


if __name__ == '__main__':
    print(in_container())

Proof of concept:

$ docker run --rm -it --mount type=bind,source=${PWD}/incontainer.py,target=/tmp/script.py python:3 python /tmp/script.py
True
blakev
  • 4,154
  • 2
  • 32
  • 52
3

In a docker container, entries /proc/self/cgroup are mounted to cgroups on the host.

e.g. in a container

# awk -F: '/cpuset/' /proc/self/cgroup
3:cpuset:/docker/22bd0c154fb4e0d1b6c748faf1f1a12116acc21ce287618a115ad2bea41256b3

whereas, the same on the host

$ awk -F: '/cpuset/' /proc/self/cgroup
3:cpuset:/

Using something in the shell for a low profile test

is_running_in_container() {
  awk -F: '/cpuset/ && $3 ~ /^\/$/ { c=1 } END{ exit c }' /proc/self/cgroup
}

if is_running_in_container; then
  echo "Aye!! I'm in a container"
else 
  echo "Nay!! I'm not in a container"
fi
shalomb
  • 3,456
  • 2
  • 23
  • 19
2

My answer only applies for Node.js processes but may be relevant for some visitors who stumble to this question looking for a Node.js specific answer.

I had the same problem and relying on /proc/self/cgroup I created an npm package for solely this purpose — to detect whether a Node.js process runs inside a Docker container or not.

The containerized npm module will help you out in Node.js. It is not currently tested in Io.js but may just as well work there too.

Martin Tajur
  • 5,242
  • 1
  • 20
  • 15
  • Thanks for this module, seems to be a couple of open fixes pending - are you still maintaining this? – stevokk Aug 02 '18 at 08:51
  • This module is no longer maintained. Additionally, a Node.js module that detects non-Docker container environments is needed. – Jeff Apr 29 '22 at 14:12
2

I have translated JJC's answer into ruby

def in_docker
  File.open('/proc/1/cgroup', 'rt') do |f|
    contents = f.read
    return contents =~ /docker/i || contents =~ /kubepod/i
  end
rescue StandardError => e
  p 'Local development'
  p e
  false
end
Souradeep Nanda
  • 3,116
  • 2
  • 30
  • 44
2

A little bit of off topic, you can check if you're in a container or not in two ways:

  1. cat /proc/1/environ|tr "\0" "\n"|grep container : you will see container variable if you're in a container.

  2. ps -ef | grep '\[' : you will see nothing but only the grep process when you're in a container, which means there's no kernel process that you can see (e.g. [kthreadd]). Caution: normal macOS also don't show kernel processes.

reference: this linux quiz page

Roeniss
  • 386
  • 5
  • 16
1

Here's a solution in Ruby,

# Usage: DockerHelper.running_in_docker?
module DockerHelper
  extend self

  def running_in_docker?
    !!(File.read("/proc/1/cgroup") =~ %r[^\d+:\w+:/docker/]) # !! => true/false
  rescue Errno::ENOENT
    false
  end
end

If you like tests with your code, here's a spec in the gist.

tantrix
  • 1,248
  • 12
  • 14
1

The golang code get pid container_id and you can get map container_id get docker image

func GetContainerID(pid int32) string {
    cgroupPath := fmt.Sprintf("/proc/%s/cgroup", strconv.Itoa(int(pid)))
    return getContainerID(cgroupPath)
}

func GetImage(containerId string) string {
    if containerId == "" {
        return ""
    }
    image, ok := containerImage[containerId]
    if ok {
        return image
    } else {
        return ""
    }
}
func getContainerID(cgroupPath string) string {
    containerID := ""
    content, err := ioutil.ReadFile(cgroupPath)
    if err != nil {
        return containerID
    }
    lines := strings.Split(string(content), "\n")
    for _, line := range lines {
        field := strings.Split(line, ":")
        if len(field) < 3 {
            continue
        }
        cgroup_path := field[2]
        if len(cgroup_path) < 64 {
            continue
        }
        // Non-systemd Docker
        //5:net_prio,net_cls:/docker/de630f22746b9c06c412858f26ca286c6cdfed086d3b302998aa403d9dcedc42
        //3:net_cls:/kubepods/burstable/pod5f399c1a-f9fc-11e8-bf65-246e9659ebfc/9170559b8aadd07d99978d9460cf8d1c71552f3c64fefc7e9906ab3fb7e18f69
        pos := strings.LastIndex(cgroup_path, "/")
        if pos > 0 {
            id_len := len(cgroup_path) - pos - 1
            if id_len == 64 {
                //p.InDocker = true
                // docker id
                containerID = cgroup_path[pos+1 : pos+1+64]
                // logs.Debug("pid:%v in docker id:%v", pid, id)
                return containerID
            }
        }
        // systemd Docker
        //5:net_cls:/system.slice/docker-afd862d2ed48ef5dc0ce8f1863e4475894e331098c9a512789233ca9ca06fc62.scope
        docker_str := "docker-"
        pos = strings.Index(cgroup_path, docker_str)
        if pos > 0 {
            pos_scope := strings.Index(cgroup_path, ".scope")
            id_len := pos_scope - pos - len(docker_str)
            if pos_scope > 0 && id_len == 64 {
                containerID = cgroup_path[pos+len(docker_str) : pos+len(docker_str)+64]
                return containerID
            }
        }
    }
    return containerID
}
wcc526
  • 3,915
  • 2
  • 31
  • 29
0

Docker is evolving day by day, so we can't say for sure if they are going to keep .dockerenv .dockerinit in the future.

In most of the Linux flavours init is the first process to start. But in case of containers this is not true.

#!/bin/bash
if ps -p1|grep -q init;then  
  echo "non-docker" 
else 
  echo "docker" 
fi
Govind Kailas
  • 2,645
  • 5
  • 22
  • 24
  • 6
    @RomanTrofimov LXC/Docker doesn't either. What a funny comment. – abourget Mar 03 '17 at 17:41
  • 1
    It does not work in centos 7 as well. When I run in my host machine it says docker. Looks like systemd is running as process id 1 – Venkateswara Rao Jan 26 '18 at 05:02
  • @VenkateswaraRao - This must be run inside the container. The intent is to find out if you are inside a docker container or not. – Govind Kailas Jan 30 '18 at 04:43
  • 1
    @GovindKailas: The problem is that this assumes that the normal PID one is `init`, which is not true on `systemd` or `launchd` based systems... – Gert van den Berg Feb 16 '18 at 14:24
  • 3
    @SamThomas: launchd, upstart, Solaris SMF, systemd, Sys V style init, BSD style init (these two and some others might call their PID 1 `init` though), OpenRC, initng, runit. [See here](https://en.wikipedia.org/wiki/Init#Replacements_for_init). Most modern Linux-based systems would use `systemd`, some older ones, upstart.... All modern OS X systems would use `launchd` – Gert van den Berg May 23 '18 at 08:16
  • even systemd os (Ubuntu) call /lib/systemd/systemd /sbin/init when its pid 1 – teknopaul Jun 22 '21 at 21:09
-7

Maybe this do the trick:

if [ -z $(docker ps -q) ]; then
    echo "There is not process currently running"
else
    echo "There are processes running"
fi

Is that what you want? Hope it helps =)

  • 2
    No `docker` binary is available from inside of container, obviously. – toriningen Jul 05 '18 at 17:02
  • 3
    Umm, this would fail in situations (e.g. gitlab docker-in-docker) where the controlling container has `docker` and access to the hosts' docker socket. – shalomb Jul 15 '18 at 21:02
  • 2
    yeah, you're right, of course there isn't ^^. I got the wrong interpretation about the question back at the time when I read it. Thank you, Shalomb. – Leonardo Da Vinci Apr 16 '20 at 18:51