Can you run Docker containers on Kubernetes?
Kubernetes supports multiple container runtimes and does not use Docker as its default engine. Since version 1.20, Kubernetes deprecated direct Docker support in favor of containerd and CRI-O, which are compliant with the Container Runtime Interface (CRI). Docker itself does not implement CRI, so Kubernetes cannot directly manage Docker containers without an additional layer.
However, it’s still possible to run Docker containers on Kubernetes by building Docker images and using a CRI-compatible runtime like containerd to run them. For local development environments like Docker Desktop, Docker containers are fully supported because Docker includes an integrated Kubernetes cluster that handles this compatibility. This allows teams to build, push, and deploy Docker containers using familiar tools while using Kubernetes’ orchestration features.
Deploying a Dockerized application on Kubernetes involves several key steps:
- Containerize your application with Docker: Create a Dockerfile that defines how to build your application’s Docker image. Build the Docker image using the
docker buildcommand. - Push the Docker image to a container registry: Tag your Docker image with the registry’s address and a version tag (e.g.,
docker tag my-image my-registry/my-repo/my-image:v1.0). Log in to your chosen container registry and push the tagged image to the registry usingdocker push. - Define Kubernetes resources: Create a Kubernetes Deployment YAML file that specifies which Docker image to use, the number of replicas, resource limits, and environment variables. Create a Kubernetes Service YAML file to expose your application within the cluster or to external traffic.
- Deploy to Kubernetes: Ensure you have a Kubernetes cluster running and
kubectlconfigured to interact with it. Apply the Kubernetes YAML files to your cluster usingkubectl apply -f your-deployment.yamlandkubectl apply -f your-service.yaml. - Verify and access the application: Check the status of your deployments and services using
kubectl get deploymentsandkubectl get services. Access your application based on the Service type you configured.
This is part of a series of articles about software deployment.
Benefits of deploying Docker on Kubernetes
Using Docker images on Kubernetes provides several operational and development advantages compared to using only the default container engines like containerd or CRI-O:
- Consistent image format and workflow: Docker remains the de facto standard for building container images. Teams can keep using familiar Dockerfiles,
docker build, anddocker pushworkflows without learning a new image format. Kubernetes pulls and runs these images via containerd or CRI-O seamlessly. - Large ecosystem and registry support: Docker Hub and other Docker-compatible registries host a vast catalog of ready-to-use images. This accelerates development by reducing the need to build every image from scratch.
- Cross-environment portability: Docker images are portable across environments (local dev, staging, and production) regardless of whether Kubernetes uses Docker, containerd, or CRI-O under the hood.
- Integration with existing CI/CD pipelines: Most CI/CD tools already support Docker commands natively. This avoids rework when integrating Kubernetes deployments into existing pipelines.
- Smooth local-to-cluster transition: Tools like Docker Desktop provide an integrated Kubernetes cluster for local testing. Developers can run the same Docker image locally and deploy it directly to a remote Kubernetes cluster without modifications.
Key steps involved in deploying Docker images to Kubernetes
1. Containerize your application with Docker
The first step in deploying an application on Kubernetes is to package it into a Docker container. Developers start by writing a Dockerfile, which contains a set of instructions specifying the operating system, application code, dependencies, and any required environment variables. This file serves as a blueprint for building the Docker image, ensuring that the application and everything it needs to run are included in a predictable, portable format.
Once the Dockerfile is ready, developers execute the docker build command to create the image, which results in a self-contained environment for the application. This process isolates the application from underlying system differences, reducing the “it works on my machine” problem and simplifying the movement of the application between development, staging, and production environments.
2. Push the Docker image to a container registry
After building the Docker image, the next step is to upload it to a container registry such as Docker Hub, Google Container Registry, or Amazon ECR. A container registry acts as a centralized repository for storing and versioning images, making them accessible from anywhere. Developers log into the registry, tag their image appropriately, and use the docker push command to upload the image to the remote registry.
Storing images in a registry ensures that the Kubernetes cluster nodes can access the latest version of the application image during deployments or scaling events. It also helps teams manage image security, rollbacks, and traceability by providing version controls and audit histories for all pushed images.
3. Define Kubernetes resources
With the Docker image available in a registry, the next step is defining Kubernetes resources in YAML or JSON configuration files. These files declare the desired application state, such as the number of container instances (replicas), the container image to use, necessary environment variables, network ports, and storage volumes. Commonly used resource definitions include deployments, services, ConfigMaps, and secrets.
Defining resources this way enables consistency, repeatability, and easy version control in source repositories. Changes to the application deployment can be tracked using version control systems, and applying the configuration to a cluster with tools like kubectl ensures that the intended state is achieved without manual intervention.
4. Deploy to Kubernetes
To deploy the application on Kubernetes, operators use the kubectl apply or kubectl create commands to send resource definitions to the cluster. Kubernetes reads the manifests and creates the specified pods, deployments, and services. The cluster’s schedulers assign pods to available nodes, pull the Docker image from the registry, and start the containers as described.
Note: The actual containers running on Kubernetes will typically not use Docker as their container engine, but they will be based on the original Docker image.
This deployment process is automated and declarative. Kubernetes constantly monitors the state of deployed resources, ensuring that the actual state matches the desired configuration. If containers fail or nodes become unhealthy, Kubernetes takes corrective action by rescheduling pods or restarting containers, helping maintain application availability.
5. Verify and access the application
After deployment, it’s essential to verify that the application is running as expected. Commands like kubectl get pods and kubectl describe pod can be used to check pod status, view logs, and diagnose issues if containers fail to start. These tools provide detailed insights into container lifecycle events and runtime errors.
To make the application accessible, operators typically expose it via a Kubernetes service, which defines a policy for accessing one or more pods. Services can be configured as ClusterIP (internal-only), NodePort, or LoadBalancer (external access). Once exposed, users can access the application through the assigned IP address or DNS name, and further testing or integration can proceed.
Quick tutorial: Deploying Docker on Kubernetes with Docker Desktop
To deploy Docker containers on Kubernetes using Docker Desktop, you can use the built-in Kubernetes support included in the Docker Desktop application. This setup is ideal for local development and testing, offering a lightweight Kubernetes environment without requiring a separate installation. Instructions are adapted from the Docker documentation.
Step 1: Enable Kubernetes in Docker Desktop
Start by opening Docker Desktop and navigating to Settings > Kubernetes. Enable the Kubernetes option by toggling Enable Kubernetes, then click Apply. Docker Desktop will begin installing and configuring the Kubernetes control plane as containerized services.
During this process, Docker generates the necessary certificates, installs internal Kubernetes components, and boots up the cluster. The Kubernetes CLI tool kubectl is also installed automatically, allowing you to interact with the cluster from the command line.
Note: On macOS and Windows, kubectl is installed at a default location, but you may need to add it to your system’s PATH. On Linux, you must install kubectl manually.
Step 2: Verify the Kubernetes cluster
Once setup is complete, confirm that the cluster is running by executing:
kubectl get nodes
You should see a node named docker-desktop with a Ready status. This node represents the single-node or multi-node cluster running within Docker Desktop.
To ensure you’re interacting with the Docker Desktop cluster, check the current Kubernetes context:
kubectl config get-contexts
kubectl config use-context docker-desktop
Step 3: Choose a provisioning method
Docker Desktop supports two provisioning modes for Kubernetes clusters: kubeadm and kind.
- kubeadm: Available in all Docker Desktop versions (4.0+). It sets up a single-node cluster and supports both Docker and containerd image stores.
- kind: Available in Docker Desktop 4.38+ for signed-in users. It supports multi-node clusters, faster provisioning, Kubernetes version selection, and is compatible with Enhanced Container Isolation (ECI). However, it requires the containerd image store.
Choose your provisioning method based on your security and scalability needs. You can select the method in the Settings > Kubernetes section before enabling the cluster.
Step 4: Deploy a Dockerized application
Build your application into a Docker image and push it to a registry accessible from the Kubernetes cluster. Create Kubernetes manifest files (YAML) defining resources like Deployments and Services, then deploy them with:
kubectl apply -f your-app.yaml
Kubernetes will pull the image from the registry, schedule the pod(s) onto the docker-desktop node, and start the containers.
Step 5: Access and inspect the application
To access your deployed service, expose it using a Service of type NodePort or LoadBalancer. You can then use kubectl get svc to view the assigned port or external IP. To inspect the running Kubernetes containers, enable the Show system containers (advanced) option in the Kubernetes settings or use:
docker ps
Step 6 (optional): Use a custom image registry
If you need to use a private registry instead of Docker Hub for Kubernetes control plane images, configure the KubernetesImagesRepository setting. This is useful in restricted environments where external registry access is not permitted.
You must mirror the required images into your registry, stop the cluster, update the registry setting, and then restart Docker Desktop. This applies only to Kubernetes control plane components, not user-deployed workloads.
Best practices for Docker deployment on Kubernetes
Here are some useful practices to keep in mind when deploying Docker on Kubernetes.
1. Use lightweight and minimal base images
Choose minimal base images like alpine, busybox, or distroless to reduce your Docker image size. These images contain fewer packages and libraries, which decreases the overall attack surface and makes the image more secure. Smaller images also result in faster transfer times when pulling from registries, improving deployment speed and reducing resource use on nodes.
Additionally, smaller images are easier to cache and replicate across environments. This becomes increasingly important at scale, where hundreds of containers may be deployed across clusters. Avoid including development tools, package managers, or shells in your final image unless they are essential for the application’s operation.
2. Apply multi-stage Docker builds
Multi-stage builds allow you to use different stages within a single Dockerfile to separate the build process from the final runtime image. The initial stages can include all necessary tools for compiling code, running tests, or performing static analysis, while the final stage copies only the required binaries and configuration into a clean base image.
This pattern helps you keep runtime images lean and production-ready, removing unnecessary files and dependencies. For example, a Go application might be compiled in a golang base image but served from a scratch or alpine image. This not only optimizes performance but also improves security by excluding tools that could be exploited.
3. Follow the principle of least privilege
Containers should not run as the root user unless absolutely necessary. Running as non-root limits the damage that can be done if the container is compromised. In the Dockerfile, create a specific user with limited permissions using the USER directive, and ensure file ownership and permissions are correctly configured.
In Kubernetes, you can further enforce security by using PodSecurityPolicies (deprecated) or Pod Security Standards, and by configuring securityContext fields such as runAsNonRoot, readOnlyRootFilesystem, and capabilities. These settings help isolate workloads and align with security compliance requirements.
4. Implement health checks
Health checks are crucial for maintaining the stability and availability of your application. Kubernetes provides livenessProbe to detect when a container is no longer functioning correctly and should be restarted, and readinessProbe to determine when a container is ready to start receiving traffic.
Use HTTP endpoints, TCP ports, or command executions in your probes to monitor application health. For example, a readinessProbe might check a /health endpoint to ensure a web server has finished initializing. Properly configured probes prevent broken containers from disrupting service and allow rolling updates to proceed safely.
5. Automate deployments with CI/CD
CI/CD pipelines automate the process of building Docker images, pushing them to registries, and applying Kubernetes manifests to deploy applications. This reduces manual intervention, eliminates human error, and ensures that each change is tested and deployed in a consistent way.
Tools like Octopus Deploy and Argo CD support integration with Kubernetes and container registries. Pipelines should include steps for linting manifests, scanning images for vulnerabilities, and performing integration tests. Tagging and versioning images properly ensures traceability and makes rollbacks more reliable during incidents.
Help us continuously improve
Please let us know if you have any feedback about this page.

