---
title: "A Tutorial on Installing and Configuring Kubernetes on Ubuntu 22.04"
description: "A complete step-by-step guide to installing and configuring Kubernetes on Ubuntu 22.04, including the Containerd environment, `kubeadm` initialization, Pod network installation, and deploying your first container."
canonical_url: "https://blog.markkulab.net/en/post/intsll-kubernetes-in-ubuntu"
author: "Mark Ku"
author_url: "https://blog.markkulab.net/en/author/mark-ku"
site: "Mark Ku's Tech Notes"
date_published: "2024-11-11 01:01:35 +0800"
category: "DevOps"
tags: ["ubuntu", "docker", "kubernetes", "kubeadm", "containerd", "devops", "linux"]
language: "en"
license: "CC BY 4.0"
license_url: "https://creativecommons.org/licenses/by/4.0/"
attribution: "when reusing or quoting, credit the author and link back to the original"
---

# A Tutorial on Installing and Configuring Kubernetes on Ubuntu 22.04

## Introduction
Previously, I [enabled Kubernetes on Windows Docker Desktop](https://blog.markkulab.net/jenkins-deploy-kubernetes-with-docker-for-windows/), but because Windows Kubernetes only supports a single node, isn't very stable, and consumes a lot of resources, I decided to set up a home lab with the more mainstream Linux version of Kubernetes.

## Environment
* Ubuntu 22.04
* [Docker pre-installed](https://blog.markkulab.net/enable-docker-2375-port-in-ubuntu22/)

## Install Required Software


Run the following commands to update the system and install packages:
```bash
sudo apt-get update
sudo apt-get upgrade
sudo apt-get install -y net-tools git
```

- Use Chrony to configure time synchronization
Chrony provides faster synchronization, higher precision, and uses fewer resources.

Install Chrony
```bash
sudo apt update
sudo apt install chrony
```
Modify Chrony configuration
```bash
sudo vim /etc/chrony/chrony.conf
```
```config
pool 0 asia.pool.ntp.org iburst
pool 1.pool.ntp.org iburst
pool 2.pool.ntp.org iburst
pool 3.pool.ntp.org iburst
pool 4.pool.ntp.org iburst
```
Restart and test
```bash
sudo systemctl start chrony
sudo systemctl enable chrony
sudo chronyc -a makestep // 立刻同步時間
chronyc tracking // 檢查 Chrony 同步狀態
chronyc sources -v // 還可以查看連線的 NTP 伺服器列表及其同步狀態
timedatectl status //  確認系統時間狀態

```
- Disable the firewall:
```bash
  # 允許 Kubernetes API server 通信
  sudo ufw allow 6443/tcp   # Kubernetes API server

  # etcd 集群通訊
  sudo ufw allow 2379/tcp   # etcd client communication
  sudo ufw allow 2380/tcp   # etcd server-to-server communication

  # kubelet API
  sudo ufw allow 10250/tcp  # Kubelet API
  sudo ufw allow 10255/tcp  # Read-only Kubelet API (可選)
  sudo ufw allow 10248

  # Controller manager 和 scheduler
  sudo ufw allow 10251/tcp  # kube-scheduler
  sudo ufw allow 10252/tcp  # kube-controller-manager

  # Kubernetes DNS (CoreDNS)
  sudo ufw allow 53/tcp     # DNS TCP
  sudo ufw allow 53/udp     # DNS UDP

  # NodePort services (範圍可自定義，預設 30000-32767)
  sudo ufw allow 30000  # NodePort services

  # 啟用防火牆
  sudo ufw enable
  sudo ufw status verbose
```

- Disable OS Swap to prevent containers from being killed when memory is low:
```bash
sudo swapoff -a
sudo sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab
```


## Install Kubernetes
### Add Kernel Parameters
```bash
sudo tee /etc/modules-load.d/containerd.conf <<EOF
overlay
br_netfilter
EOF
```
```bash
sudo modprobe overlay
sudo modprobe br_netfilter
```
```bash
sudo tee /etc/sysctl.d/kubernetes.conf <<EOF
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
EOF
```

Load the configuration changes:
```bash
sudo sysctl --system
```

## Install the Containerd Runtime
Containerd is a lightweight container runtime that manages basic functions like starting and stopping containers.
```bash
sudo apt install -y curl gnupg2 software-properties-common apt-transport-https ca-certificates

sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmour -o /etc/apt/trusted.gpg.d/docker.gpg
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"

sudo apt update
sudo apt install -y containerd.io

containerd config default | sudo tee /etc/containerd/config.toml >/dev/null 2>&1
sudo sed -i 's/SystemdCgroup \= false/SystemdCgroup \= true/g' /etc/containerd/config.toml

sudo systemctl restart containerd
sudo systemctl enable containerd
```

## Install Kubernetes Components
```bash
sudo apt-get update
# apt-transport-https may be a dummy package; if so, you can skip that package
sudo apt-get install -y apt-transport-https ca-certificates curl gpg

curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.29/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg

# This overwrites any existing configuration in /etc/apt/sources.list.d/kubernetes.list
echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.29/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list

sudo apt-get update
sudo apt-get install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl
```

## Initialize the Kubernetes Master Node

Reload
```bash
sudo systemctl daemon-reload && sudo systemctl restart kubelet
sudo sysctl --system
```
Restart Docker:
```bash
sudo systemctl daemon-reload && sudo systemctl restart docker
sudo systemctl enable kubelet && sudo systemctl restart kubelet
```

## Initialize kubeadm
```bash
sudo kubeadm init
```

After initialization succeeds, follow the prompt to copy the kubectl configuration for your user:
```bash
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
```
![kubeadm init result](https://blog.markkulab.net/content/markku/posts/intsll-kubernetes-in-ubuntu/images/kubeadm-init-result.png)

## Install the Pod Network
```bash
kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.25.0/manifests/calico.yaml
```

## As prompted, run the following command on other machines to add them as worker nodes
```bash
kubeadm join 192.168.50.50:6443 --token <TOKEN> --discovery-token-ca-cert-hash sha256:<HASH>
```

## Check the Status of All Nodes
```bash
kubectl get nodes
```
Check detailed node status
```bash
kubectl describe node master-node
```

![kubectl get nodes](https://blog.markkulab.net/content/markku/posts/intsll-kubernetes-in-ubuntu/images/kubectl-get-node.png)

## Create the First Kubernetes Container
Prepare the Persistent Volume path:
```bash
sudo mkdir /var/docker-pvc
sudo chmod 777 /var/docker-pvc
```
### Deployment Configuration File `deployment.yaml` (On Windows, you don't need to specify the Persistent Volume location, but on Linux, it's required, e.g., `hostPath: path: /var/docker-pvc`)

Create the Kubernetes deployment file
```
sudo vim deployment.yaml
```

```yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: uptime-kuma-pv
spec:
  capacity:
    storage: 1Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: /var/docker-pvc

---

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: uptime-kuma-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
  volumeName: uptime-kuma-pv

---

apiVersion: apps/v1
kind: Deployment
metadata:
  name: uptime-kuma-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: uptime-kuma
  template:
    metadata:
      labels:
        app: uptime-kuma
    spec:
      containers:
      - name: uptime-kuma
        image: louislam/uptime-kuma:1
        ports:
        - containerPort: 3001
        volumeMounts:
        - name: uptime-kuma-volume
          mountPath: /app/data
      volumes:
      - name: uptime-kuma-volume
        persistentVolumeClaim:
          claimName: uptime-kuma-pvc

---

apiVersion: v1
kind: Service
metadata:
  name: uptime-kuma-service
spec:
  type: NodePort
  selector:
    app: uptime-kuma
  ports:
  - port: 3001
    targetPort: 3001
    nodePort: 30000
```
Deploy the application
```
kubectl apply -f ./deployment.yaml
```

## Remove the Control Plane Taint (for single-node environments)
```
kubectl taint nodes --all node-role.kubernetes.io/control-plane-
```
This command is primarily used to address a resource scheduling issue on Kubernetes control plane nodes. By default, Kubernetes applies a "taint" to control plane nodes (usually the master node) to prevent general workloads (Pods) from being scheduled on them. This ensures that the control plane nodes can focus on managing the cluster and remain stable.
## Final Result
Now, if you visit localhost:30000, you should see this screen:
![final result](https://blog.markkulab.net/content/markku/posts/intsll-kubernetes-in-ubuntu/images/final-result.png)

## Appendix - If the Installation Fails or to Reinstall Kubernetes
```bash
sudo kubeadm reset -f
sudo rm -rf ~/.kube
sudo rm -rf /etc/kubernetes /var/lib/etcd /var/lib/kubelet /var/lib/kubeadm /etc/cni /opt/cni
sudo systemctl status docker
sudo systemctl status containerd
sudo apt-get remove --purge -y kubeadm kubectl kubelet kubernetes-cni cri-tools
sudo apt-get autoremove -y
sudo dpkg --purge kubectl kubeadm kubelet
sudo apt-get autoremove --purge -y
sudo rm -rf /etc/kubernetes /var/lib/etcd /var/lib/kubelet /etc/cni /opt/cni ~/.kube
sudo rm /etc/apt/sources.list.d/kubernetes.list
sudo apt-get update
```

## References
* [Complete Guide to Kubernetes Cluster Setup on Ubuntu 22.04 LTS : A Step-by-Step Tutorial for DevOps](https://medium.com/@kvihanga/how-to-set-up-a-kubernetes-cluster-on-ubuntu-22-04-lts-433548d9a7d0)
* [How to Set Up a Kubernetes Cluster on Ubuntu 22.04 in 5 Minutes](https://blog.kkbruce.net/2023/08/5min-ubuntu-2204-kubernetes-cluster.html)
* [How to Install Kubernetes on Ubuntu 22.04](https://phoenixnap.com/kb/install-kubernetes-on-ubuntu)
* [Creating Master+Node Nodes (Troubleshooting Node NotReady Status)](https://blog.csdn.net/nmjhehe/article/details/99191632)

---

## About this article and its author

Originally published on [Mark Ku's Tech Notes](https://blog.markkulab.net/en/post/intsll-kubernetes-in-ubuntu)

License: [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/) — when reusing or quoting, credit the author and link back to the original

### About the author

**[Mark Ku](https://blog.markkulab.net/en/author/mark-ku)** — Software Solution Provider

- 10+ years senior software engineer, now an AI Builder
- Focused on large-platform architecture — North-American e-commerce, AI SaaS subscription billing
- Combining AI Agents and automation to build evolvable product foundations

### Free tools built by the author

All of these are free to use:

- [Free PDF Sign Tool](https://blog.markkulab.net/en/tools/pdf-sign): Online PDF sign tool — draw, type, or upload a signature, then drag, resize, and download. Everything runs in your browser; nothing is uploaded.
- [VS Code Refactory](https://blog.markkulab.net/en/tools/refactory): Refactory is a VS Code refactoring extension: 34 actions plus a 37-rule code-smell inspection layer with a Code Health dashboard, across 18 languages, backed by 534 tests. It learns your repo's conventions: where interfaces live, where DI is registered, whether 'use client' belongs. It ranks files by git churn × complexity so you know what to fix first, and hands any smell to the Claude Code already on your machine. Free to use, and your source never leaves your computer.
- [DB-Kit Database Manager](https://blog.markkulab.net/en/tools/db-kit): DB-Kit is a lightweight, cross-platform database manager built with Tauri + Rust + React. Manage MySQL, MariaDB, PostgreSQL, SQL Server, Oracle, SQLite, MongoDB, Redis, Kafka, Elasticsearch and RabbitMQ from one consistent interface: passwords encrypted in the OS keychain, SSH tunnels, full CRUD, a visual query builder, stacked multi-statement result sets, cross-connection data transfer and compare/sync, Excel / CSV import & export, visualized execution plans, ER diagrams, scheduled backups, SQL stress testing with p50–p99 latency percentiles, a 15-rule SQL review engine, Kafka message browsing with monitoring & alerts, a bilingual UI (Traditional Chinese / English), a built-in AI assistant (natural-language SQL, AI review and tuning advice) and the dbk CLI. Free and open source (MIT), with installers for Windows, macOS and Linux.
- [VS Code Super Mermaid](https://blog.markkulab.net/en/tools/super-mermaid): Super Mermaid is a VS Code extension for beautiful Mermaid diagrams out of the box: auto-colored live preview, mouse pan & zoom, high-res PNG / SVG export, 21 templates and multiple themes. Free and open source (MIT).
- [React Super Mermaid](https://blog.markkulab.net/en/tools/react-super-mermaid): react-super-mermaid is an open-source React component library: render beautiful Mermaid diagrams with a single <MermaidViewer>, with built-in colorful / sketch themes, pan & zoom, in-diagram search, and high-res SVG / PNG export. Lightweight, SSR-safe, fully typed. Free and open source (MIT).
- [Jira / Confluence Super Mermaid](https://blog.markkulab.net/en/tools/jira-super-mermaid): An Atlassian Forge app: write Mermaid syntax directly inside a Jira issue or a Confluence page and get flowcharts, sequence diagrams, state machines and Gantt charts. 11 diagram types, SVG / PNG export, light and dark themes, full CJK support. Runs on Atlassian: your diagrams live in your own site and the app calls no third-party service. Free, coming soon to the Atlassian Marketplace.
- [Mermaid Live Preview](https://blog.markkulab.net/en/tools/mermaid-preview): Write Mermaid in your browser, see it render instantly, and share the whole diagram as a single link. No sign-up, nothing uploaded to a server, and mermaid.live share links work as-is.
- [React Intl Phone Number](https://blog.markkulab.net/en/tools/react-intl-phone-number): react-intl-phone-number is an open-source React component: framework-agnostic and antd-free, with E.164 in/out, a searchable flag / country-code dropdown, configurable validation levels (strict / mobile-strict / loose), themeable CSS, and i18n — phone logic powered by google-libphonenumber. Lightweight and fully typed. Free and open source (MIT).
- [Uptime Kuma Cluster](https://blog.markkulab.net/en/tools/uptime-kuma-cluster): Turn single-node Uptime Kuma into a highly available cluster: OpenResty + Lua smart load balancing, shared MariaDB state, health checks and automatic failover, plus cluster-management REST APIs. One Docker Compose command to start. Free and open source (MIT).
- [Special Education](https://blog.markkulab.net/en/education): Learning materials crafted for special education students

### Daily podcasts

- [Mark's Tech Insights — Daily AI News](https://blog.markkulab.net/en/category/tech-news): Daily curated AI and tech trends. Catch the latest developments via audio summaries — covering AI applications, software architecture, DevOps, and engineering practice. — RSS: https://blog.markkulab.net/feed.xml
- [AI股市蝦聊](https://blog.markkulab.net/en/category/ai-stock-chat): Every trading day, an AI-analyzed take on the Taiwan stock market, delivered as a two-host conversation covering the session and the next-day outlook. — RSS: https://blog.markkulab.net/ai-stock-chat/feed.xml
- [開源好物週報](https://blog.markkulab.net/en/category/open-source-weekly): A weekly two-host pick of free open-source tools surfaced from real Hacker News, GitHub, and Reddit buzz — what pain they solve and the fastest way to get started. — RSS: https://blog.markkulab.net/open-source-weekly/feed.xml

### Newsletter

[Subscribe to the newsletter](https://blog.markkulab.net/en/subscribe) — Be the first to know about new posts. No spam, unsubscribe anytime.
