1. Overview

Sometimes, while running a script or program, we need to know whether we’re running it inside a container. This is because some commands aren’t relevant to the container environment. For example, if our script tries to change some OS kernel parameters using the sysctl command, it won’t work in a docker container.

In this tutorial, we’ll talk about some ways to determine if we’re inside a Linux container or not.

2. Using Control Groups

We can use control groups to determine if a process is running inside a Docker container.

The control group is a Linux kernel feature to control or track resource usage for a group of processes. Here, by resources, we mean system resources like memory, CPU, and IO devices.

Linux represents these control groups as a pseudo-filesystem. The Linux containers share the kernel of their host. So, Linux uses a separate namespace for them in the control groups.

The method we use to determine if a process is running inside a Docker container varies depending on whether our system is using control group v1 or v2. This is because their respective file layout is significantly different.

On control group v1, we can identify if a process is running in a Docker or LXC container from the control group of the init process:

$ docker run -it --rm --name myubuntu ubuntu:latest
root@184977d6a721:/# cat /proc/1/cgroup
14:name=systemd:/docker/184977d6a721a33b0df3855d197b222496d4d049ed118e3dc5e42ca3d22377b5
13:rdma:/
12:pids:/docker/184977d6a721a33b0df3855d197b222496d4d049ed118e3dc5e42ca3d22377b5
11:hugetlb:/docker/184977d6a721a33b0df3855d197b222496d4d049ed118e3dc5e42ca3d22377b5
10:net_prio:/docker/184977d6a721a33b0df3855d197b222496d4d049ed118e3dc5e42ca3d22377b5
9:perf_event:/docker/184977d6a721a33b0df3855d197b222496d4d049ed118e3dc5e42ca3d22377b5
8:net_cls:/docker/184977d6a721a33b0df3855d197b222496d4d049ed118e3dc5e42ca3d22377b5
7:freezer:/docker/184977d6a721a33b0df3855d197b222496d4d049ed118e3dc5e42ca3d22377b5
6:devices:/docker/184977d6a721a33b0df3855d197b222496d4d049ed118e3dc5e42ca3d22377b5
5:memory:/docker/184977d6a721a33b0df3855d197b222496d4d049ed118e3dc5e42ca3d22377b5
4:blkio:/docker/184977d6a721a33b0df3855d197b222496d4d049ed118e3dc5e42ca3d22377b5
3:cpuacct:/docker/184977d6a721a33b0df3855d197b222496d4d049ed118e3dc5e42ca3d22377b5
2:cpu:/docker/184977d6a721a33b0df3855d197b222496d4d049ed118e3dc5e42ca3d22377b5
1:cpuset:/docker/184977d6a721a33b0df3855d197b222496d4d049ed118e3dc5e42ca3d22377b5

The above command displays the contents of the control group file for the process with PID 1. This is the initialization process for a Linux-based operating system. If some of the lines start with /docker or /lxc, the process is running inside a Docker or LXC container. They’ll start with only / if the process runs in a host operating system.

Conversely, in control group v2, container IDs are exposed via the /proc/self/mountinfo directory:

$ cat /proc/self/mountinfo 
24 29 0:22 / /sys rw,nosuid,nodev,noexec,relatime shared:7 - sysfs sysfs rw
25 29 0:23 / /proc rw,nosuid,nodev,noexec,relatime shared:15 - proc proc rw
26 29 0:5 / /dev rw,nosuid,noexec,realtime shared:2 - devtmpfs udev rw,size=6007464k,nr_inodes=1501866,mode=755,inode64
680 655 254:1 /docker/containers/7a0144cee1256c539fab790199527b7051aff1b603ebcf7ed3fd436440ef3b3a/hostname /etc/hostname rw,relatime - ext4 /dev/vda1 rw
682 655 254:1 /docker/containers/7a0144cee1256c539fab790199527b7051aff1b603ebcf7ed3fd436440ef3b3a/hosts /etc/hosts rw,relatime - ext4 /dev/vda1 rw
....truncated....

Here, we can identify processes running inside a Docker container by looking at the lines that start with /docker. Processes running in an LXC container should start with /lxc.

3. Existence of .dockerenv

Another popular way is to check the existence of the .dockernev file at the root location (/):

$ echo `[ ! -f /.dockerenv ]` $?

This command checks the file’s existence and prints 1 if it’s there; otherwise, it prints 0. Docker creates this file while creating the root file system. The LXC drivers use this file to set up environment variables properly.

But, as the LXC support is already removed from the recent Docker versions, this file may not exist in the future.

4. Using CPU Scheduling Info

We can also check the scheduling information of the init process to find out if we’re inside a container:

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

This command extracts the first line of the scheduling overview for the process with PID 1. For the Linux containers, it displays the command of the main process. For Ubuntu or CentOS, it’s bash.

However, if the process isn’t in a container, it displays init – the initialization process of the OS:

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

In some Linux distributions like CentOS and Debian, the initialization process is systemd****. So, for them, it shows system instead of init.

5. Using a Custom Environment Variable

If we’re responsible for running the containers, there’s one easier and more effective way. We can pass an environment variable while running our Docker containers. This variable will denote the environment as a container environment:

$ docker run -it -d --name myubuntu -e OS_ENV=container ubuntu:latest
e4b26a308e0ab0ae6a976b7866004d9358920d195672f734701a02724494a8c6

Here, we pass the environment variable OS_ENV with value “container” using the -e argument. Now, we can start a new Bash shell in the container and identify the environment through the variable:

$ docker exec -it myubuntu /bin/bash
root@e4b26a308e0a:/# echo $OS_ENV
container

For the host operating system, this value won’t be present. So, in any program or script, we can decide the environment based on the presence of this variable.

6. Conclusion

In this article, we’ve learned how to recognize if the current environment is inside a container or not. While discussing the different methods, we’ve also noted any downsides and in which scenario they might be applicable.