Podman exec lets you run commands inside a container that’s already running, essentially giving you a shell or a specific tool within that isolated environment.
Let’s see it in action. Imagine you have a simple nginx container running:
$ podman run -d --name my-nginx -p 8080:80 nginx
Now, you want to see the contents of the default nginx configuration directory. Instead of rebuilding the image or stopping and restarting the container with a new volume, you can just hop in:
$ podman exec my-nginx ls /etc/nginx/
conf.d
fastcgi_params
mime.types
modules
nginx.conf
proxy_params
scgi_params
uwsgi_params
Or, if you wanted to edit that configuration file, you could even start a text editor (assuming it’s installed in the container, which it often isn’t by default for minimal images like nginx):
$ podman exec -it my-nginx bash
root@<container_id>:/# vi /etc/nginx/nginx.conf
Here, -it is crucial: -i for interactive and -t for pseudo-TTY. Without them, you wouldn’t be able to interact with the shell or see output as if you were directly connected.
The core problem podman exec solves is the need for ad-hoc debugging, inspection, or command execution within a running container without disrupting its primary process. Think of it as a temporary backdoor for diagnostics. It’s not for running your application’s main workload; that’s what podman run and container orchestration are for. exec is for when your application is running, but you need to poke around inside.
Internally, podman exec leverages the same container runtime interface (CRI) that Podman uses to start containers. When you execute podman exec, Podman instructs the container runtime (like runc or crun) to create a new process within the existing container’s namespaces (PID, network, mount, etc.). This new process inherits the container’s filesystem, environment variables, and network stack, making it appear as if it’s running natively inside the container. The output and input/output streams of this new process are then piped back to your host terminal.
The most surprising thing about podman exec is that it doesn’t actually start a new container. It attaches a new process to an already running container’s execution environment. This means the process launched via exec shares the same PID namespace, network namespace, and mount namespace as the container’s main process. It’s like opening a new tab in a browser that’s already running, rather than launching a whole new browser instance.
You have fine-grained control over exec. You can specify environment variables for the command being executed using -e VAR=value, which will be added to the container’s existing environment for that specific command. You can also change the working directory for the command with -w /path/to/dir.
# Run a command in a different directory
$ podman exec -w /tmp my-container pwd
/tmp
# Set a temporary environment variable for the command
$ podman exec -e MY_TEMP_VAR=hello my-container env | grep MY_TEMP_VAR
MY_TEMP_VAR=hello
A common pitfall is forgetting that the command you’re trying to execute must exist within the container’s filesystem. If you try to podman exec my-container python and the container image doesn’t have Python installed, you’ll get a "command not found" error, not a Podman error. This is why it’s often useful to exec into a container with bash or sh first to explore and verify the existence of binaries.
The next concept you’ll likely encounter is how to manage container logs, especially when exec isn’t enough because the container is stuck or crashing before you can exec into it.