Operate containerd Safely with ctr
By the end of this guide you will be able to inspect a containerd installation, pull an image into a namespace, create and run a task, and remove the objects you created. The examples target containerd.io 2.3.5, whose client reports ctr v2.3.5. Allow about 15 minutes if containerd is already running and you have an image registry available.
The route
Jump straight to the step you need, or tick off Done means at the end.
Before you start
ctr is containerd's unsupported debug and administrative client. Its commands and options can change between releases. It talks to the containerd gRPC socket, which defaults to /run/containerd/containerd.sock, and uses the default namespace unless you choose another one.
You need the containerd.io package, a running containerd daemon, and permission to access its socket. Most commands that change images, containers, or tasks need elevated privileges, either by running as the service account that owns the socket or with sudo. The examples use sudo where that is the usual arrangement.
Checkpoint: confirm the client and daemon
Check the installed client version.
ctr versionA working connection prints both client and server version details. On this machine the client is v2.3.5. If the command reports permission denied or cannot connect to the socket, fix the service state or socket permissions before trying image operations.
Inspect the plugins known to containerd.
sudo ctr plugins lsUse
--quietfor only plugin IDs, or--detailedwhen diagnosing a plugin. A command that cannot reach the daemon cannot provide useful plugin information.
Choose a namespace deliberately
Namespaces separate containerd objects. An image pulled into default is not automatically visible in another namespace. This is a frequent source of false "image not found" errors, especially when a runtime such as Kubernetes uses its own namespace.
List existing namespaces.
sudo ctr namespaces lsCreate an isolated namespace for a manual test.
sudo ctr namespaces create ctr-demoUse that namespace for each following command.
sudo ctr --namespace ctr-demo images lsYou can write the short form
-n ctr-demo. To return to the normal namespace, omit the option or specify--namespace default.
Checkpoint: the final image list should be empty in a newly created namespace. If it is not, check that every command has the same namespace option.
Pull and inspect an image
Pull a public image into the test namespace.
sudo ctr -n ctr-demo images pull docker.io/library/alpine:3.20The registry transfer may take a little while. The command uses HTTPS by default. Do not add
--plain-httpor--skip-verifyto work around a certificate problem unless you have deliberately assessed the registry and network, because those options weaken transport checks.Confirm that the image is ready.
sudo ctr -n ctr-demo images ls sudo ctr -n ctr-demo images checkimages lsshows references known to the namespace.images checkcan restrict output to ready references with--quiet. To inspect metadata, useimages inspect docker.io/library/alpine:3.20.
Create and run a short-lived task
A container is a stored definition. A task is the running process made from that definition. Creating a container does not start a process, and starting a container creates a task that must be managed separately.
Create a container from the pulled image.
sudo ctr -n ctr-demo containers create docker.io/library/alpine:3.20 ctr-demo-shellStart a task and remove the container after it exits.
sudo ctr -n ctr-demo run --rm docker.io/library/alpine:3.20 ctr-demo-run echo 'ctr works'The
runcommand combines creation and task startup.--rmremoves that container when the command finishes. It cannot be combined with--detach, so use one mode or the other.Inspect the separately created container and start it interactively.
sudo ctr -n ctr-demo tasks list sudo ctr -n ctr-demo tasks start --detach ctr-demo-shell sudo ctr -n ctr-demo tasks ps ctr-demo-shellThe task ID is usually the container ID, but treat it as an identifier rather than assuming it. A detached task remains until you stop and delete it.
Warning: clean up in the right order
Deletion is state-changing. A task is a live process, and removing an image can affect later starts. Check the IDs before using a destructive command. Never use a broad wildcard or remove a namespace that belongs to another workload.
Stop the test task by sending the default termination signal.
sudo ctr -n ctr-demo tasks kill ctr-demo-shellDelete the task after it has stopped.
sudo ctr -n ctr-demo tasks delete ctr-demo-shellDelete the container definition.
sudo ctr -n ctr-demo containers delete ctr-demo-shellRemove the image, then remove the temporary namespace.
sudo ctr -n ctr-demo images remove docker.io/library/alpine:3.20 sudo ctr namespaces remove ctr-demo
If a task refuses to disappear, inspect it with tasks list and use tasks delete --force only when you understand why ordinary deletion failed. Forced deletion can leave the workload's process or resources in a state that needs service-level investigation.
Useful diagnostics and defaults
--address /path/to/containerd.sockselects a different gRPC socket. Verify the path with the service configuration before changing it.--connect-timeout 10slimits the connection attempt. The default is0s, meaning no client-side connection timeout is set.--timeout 30slimits the whole command. The default is also0s.--debugenables debug output in logs. Use it for a focused reproduction, since registry tracing can expose request details.
For registry failures, first verify the image reference, namespace, DNS, and certificates. The pull command supports custom CA, client certificate, and key paths when the registry requires them. Keep credentials out of shell history where possible; --user USER:PASSWORD places them directly in the command line.
Done means
ctr versionreaches the expected containerd daemon.- You can list plugins and namespaces with the privileges appropriate to the socket.
- You know which namespace contains each image and container.
- You can distinguish a container definition from its running task.
- The test task, container, image, and temporary namespace are removed, or you have recorded why they remain.