'How to determine if a process runs inside lxc/Docker?

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?



Solution 1:[1]

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.

Solution 2:[2]

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

sudo grep -qa container=lxc /proc/1/environ

Solution 3:[3]

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'm running on docker.
fi

Solution 4:[4]

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()

Solution 5:[5]

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.

Solution 6:[6]

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.

Solution 7:[7]

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('/.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

Solution 8:[8]

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.

Solution 9:[9]

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.

Solution 10:[10]

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

Solution 11:[11]

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

Solution 12:[12]

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

Solution 13:[13]

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.

Solution 14:[14]

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
}

Solution 15:[15]

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

Solution 16:[16]

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