Rust vs Go 2026 — Performance vs Simplicity

Rust and Go are two of the fastest-growing programming languages. Both were designed to solve real problems with existing languages. But they made very different tradeoffs. Go chose simplicity. Fast compilation, easy concurrency, minimal syntax. Ship code quickly. Rust chose safety and performance. Zero-cost abstractions, memory safety without garbage collection, fearless concurrency. Ship correct code. This guide compares them in depth so you can choose the right tool for your project. ...

July 15, 2026 · 9 min

Kubernetes Tutorial #6: Ingress and Gateway API — The 2026 Reality

Your Kubernetes app is running. Services expose it inside the cluster. But how does external traffic reach it? This used to be solved by Kubernetes Ingress and the popular ingress-nginx controller. But in March 2026, ingress-nginx moved to maintenance-only mode. No new features. Best-effort support only. If you are starting a new project today, use the Kubernetes Gateway API instead. It is the official, actively developed successor — built by the same SIG Network team that built Ingress. ...

July 15, 2026 · 6 min

JavaScript/TypeScript Cheat Sheet 2026 — Syntax, Types, and Patterns

Bookmark this page. Use Ctrl+F (or Cmd+F on Mac) to find what you need. This cheat sheet covers modern JavaScript (ES6+) and TypeScript essentials. Try examples at typescriptlang.org/play. Last updated: March 2026 Variables const name = "Alex"; // constant — cannot reassign let count = 0; // block-scoped — can reassign // var — avoid (function-scoped, hoisted, error-prone) TypeScript Types // Basic types let name: string = "Alex"; let age: number = 25; let active: boolean = true; let items: string[] = ["a", "b"]; let tuple: [string, number] = ["Alex", 25]; let anything: any = "skip type checking"; // avoid let safe: unknown = "must check before use"; // safer than any // Type inference — no annotation needed when obvious const name = "Alex"; // TypeScript infers string // Union types let id: string | number = "abc"; // Literal types type Direction = "north" | "south" | "east" | "west"; // Interfaces interface User { name: string; age: number; email?: string; // optional readonly id: number; // cannot modify after creation } // Type aliases type Point = { x: number; y: number }; type StringOrNumber = string | number; // Generics function first<T>(items: T[]): T | undefined { return items[0]; } Utility Types Type Description Example Partial<T> All properties optional Partial<User> Required<T> All properties required Required<User> Pick<T, K> Select properties Pick<User, "name" | "email"> Omit<T, K> Remove properties Omit<User, "id"> Record<K, V> Key-value map type Record<string, number> Readonly<T> All properties readonly Readonly<User> ReturnType<F> Function return type ReturnType<typeof fn> Parameters<F> Function parameter types Parameters<typeof fn> Functions // Arrow function const greet = (name: string): string => `Hello ${name}`; // Default parameters const greet = (name = "World") => `Hello ${name}`; // Rest parameters const sum = (...nums: number[]) => nums.reduce((a, b) => a + b, 0); // Destructured parameters const greet = ({ name, age }: User) => `${name}, ${age}`; // Function overloads (TypeScript) function format(value: string): string; function format(value: number): string; function format(value: string | number): string { return String(value); } Strings const name = "Alex"; `Hello ${name}` // template literal `Total: ${price * 1.2}` // expression `Line 1 Line 2` // multi-line "hello".toUpperCase() // "HELLO" "hello".includes("ell") // true "hello".startsWith("he") // true "hello world".split(" ") // ["hello", "world"] " hello ".trim() // "hello" "hello".padStart(10, ".") // ".....hello" "hello".repeat(3) // "hellohellohello" "hello".at(-1) // "o" (last char) "hello".replaceAll("l", "r") // "herro" Arrays const nums = [1, 2, 3, 4, 5]; // Transform nums.map(x => x * 2) // [2, 4, 6, 8, 10] nums.filter(x => x > 2) // [3, 4, 5] nums.reduce((sum, x) => sum + x, 0) // 15 [1, [2, 3], [4, 5]].flat() // flatten nested arrays: [1, 2, 3, 4, 5] nums.flatMap(x => [x, x * 10]) // [1, 10, 2, 20, ...] // Search nums.find(x => x > 3) // 4 nums.findIndex(x => x > 3) // 3 nums.includes(3) // true nums.some(x => x > 4) // true nums.every(x => x > 0) // true nums.indexOf(3) // 2 // Modify nums.push(6) // add to end nums.pop() // remove from end nums.unshift(0) // add to start nums.shift() // remove from start nums.splice(1, 2) // remove 2 items at index 1 nums.slice(1, 3) // [2, 3] (no mutation) // Sort nums.sort((a, b) => a - b) // ascending (MUTATES original!) nums.sort((a, b) => b - a) // descending (MUTATES original!) nums.toSorted((a, b) => a - b) // new sorted array (no mutation, ES2023+) nums.toReversed() // new reversed array // Create Array.from({ length: 5 }, (_, i) => i) // [0, 1, 2, 3, 4] Array.from("hello") // ["h", "e", "l", "l", "o"] [...new Set(nums)] // remove duplicates Objects // Destructuring const { name, age } = user; const { name, ...rest } = user; // rest = everything except name // Spread const updated = { ...user, age: 26 }; // clone + update const merged = { ...obj1, ...obj2 }; // merge // Computed property names const key = "name"; const obj = { [key]: "Alex" }; // { name: "Alex" } // Optional chaining user?.address?.city // undefined if any is null/undefined user?.getName?.() // call method if it exists // Nullish coalescing const name = user.name ?? "Unknown"; // "Unknown" only if null/undefined // Unlike ||, does NOT fall back on "" or 0 or false // Logical assignment operators (ES2021) x ??= 10; // x = x ?? 10 (assign if null/undefined) x &&= 10; // x = x && 10 (assign if truthy) x ||= 10; // x = x || 10 (assign if falsy) // Object methods Object.keys(obj) // ["name", "age"] Object.values(obj) // ["Alex", 25] Object.entries(obj) // [["name", "Alex"], ["age", 25]] Object.fromEntries(entries) // back to object Object.assign({}, obj1, obj2) // merge (older syntax) Async/Await // Async function async function fetchUser(id: number): Promise<User> { const response = await fetch(`/api/users/${id}`); if (!response.ok) throw new Error("Failed to fetch"); return response.json(); } // Error handling try { const user = await fetchUser(1); } catch (error) { console.error("Error:", error); } // Parallel execution const [users, posts] = await Promise.all([ fetchUsers(), fetchPosts() ]); // Race — first to resolve wins const result = await Promise.race([fetchData(), timeout(5000)]); // Promise.allSettled — wait for all, never rejects const results = await Promise.allSettled([fetchA(), fetchB()]); results.forEach(r => { if (r.status === "fulfilled") console.log(r.value); if (r.status === "rejected") console.log(r.reason); }); Modules // Named exports export const API_URL = "https://api.example.com"; export function fetchData() { } // Default export export default class UserService { } // Import import UserService from "./user-service"; import { API_URL, fetchData } from "./api"; import * as api from "./api"; // Dynamic import (lazy loading) const module = await import("./heavy-module"); Classes (TypeScript) class User { private id: number; public name: string; readonly email: string; constructor(id: number, name: string, email: string) { this.id = id; this.name = name; this.email = email; } // Shorthand constructor // constructor(private id: number, public name: string) {} greet(): string { return `Hi, I'm ${this.name}`; } } // Abstract class abstract class Shape { abstract area(): number; } class Circle extends Shape { constructor(private radius: number) { super(); } area(): number { return Math.PI * this.radius ** 2; } } Type Guards (TypeScript) // typeof if (typeof value === "string") { value.toUpperCase(); // TypeScript knows it's string } // instanceof if (error instanceof TypeError) { error.message; // TypeScript knows it's TypeError } // Custom type guard function isUser(obj: unknown): obj is User { return ( typeof obj === "object" && obj !== null && "name" in obj && "age" in obj && "id" in obj ); } // Discriminated union type Result = | { status: "success"; data: string } | { status: "error"; message: string }; function handle(result: Result) { if (result.status === "success") { result.data; // TypeScript knows data exists } } Modern TypeScript Features // satisfies — validate type without widening const config = { port: 3000, host: "localhost" } satisfies Record<string, string | number>; // config.port is still number (not string | number) // as const — immutable literal type const COLORS = ["red", "green", "blue"] as const; type Color = typeof COLORS[number]; // "red" | "green" | "blue" // template literal types type EventName = `on${Capitalize<string>}`; // "onClick", "onHover", etc. Common Mistakes == vs === — == does type coercion ("1" == 1 is true). Always use === for strict equality. The only exception: value == null checks both null and undefined. ...

July 15, 2026 · 7 min

Kubernetes Tutorial #5: Persistent Volumes and Storage in Kubernetes

Pods are ephemeral. When a Pod is deleted or rescheduled to a different node, all data written inside it is gone. This is fine for stateless apps. But databases, file uploads, and cache data need to survive Pod restarts. That is what Persistent Volumes are for. The Problem with Pod Storage By default, a container’s filesystem lives only as long as the container lives. When the container stops, the data disappears. ...

July 14, 2026 · 7 min

Kotlin vs Java 2026 — Which Should You Learn?

Kotlin or Java? This is one of the most common questions developers ask in 2026. Both languages run on the JVM. Both are used for Android, backend, and enterprise development. But they have very different philosophies. This guide compares them honestly so you can make the right choice for your career and projects. Quick Summary Category Winner Syntax and readability Kotlin Null safety Kotlin Performance Tie Learning curve (beginners) Java Learning curve (experienced) Kotlin Android development Kotlin Enterprise backend Java Job market size Java Salary per role Kotlin Community and ecosystem Java Modern language features Kotlin Tooling Tie What Is Kotlin? Kotlin is a modern, statically-typed language developed by JetBrains. It was released in 2016 and became Google’s preferred language for Android in 2019. ...

July 14, 2026 · 10 min

Linux/Terminal Commands Cheat Sheet 2026 — Essential Commands

Bookmark this page. Use Ctrl+F (or Cmd+F on Mac) to find what you need. This cheat sheet covers essential terminal commands for macOS and Linux. Last updated: March 2026 Navigation Command Description pwd Print current directory ls List files ls -la List all files with details (including hidden) ls -lh List with human-readable sizes cd /path Change directory cd ~ Go to home directory cd .. Go up one directory cd - Go to previous directory tree Show directory tree (install: brew install tree) tree -L 2 Tree with max depth 2 Files and Directories Command Description touch file.txt Create empty file mkdir mydir Create directory mkdir -p a/b/c Create nested directories cp file.txt copy.txt Copy file cp -r dir/ newdir/ Copy directory recursively mv file.txt newname.txt Rename/move file rm file.txt Delete file rm -r dir/ Delete directory recursively rm -rf dir/ Force delete (no confirmation) ln -s target link Create symbolic link cat file.txt Print file contents head -20 file.txt First 20 lines tail -20 file.txt Last 20 lines tail -f file.log Follow file in real-time (logs) less file.txt Paginated viewer (q to quit) wc -l file.txt Count lines wc -w file.txt Count words diff file1 file2 Compare two files stat file.txt File details (size, dates, permissions) Searching Command Description grep "text" file.txt Search for text in file grep -r "text" dir/ Search recursively in directory grep -i "text" file.txt Case-insensitive search grep -n "text" file.txt Show line numbers grep -c "text" file.txt Count matches grep -v "text" file.txt Show lines NOT matching find . -name "*.txt" Find files by name find . -type d -name "src" Find directories by name find . -size +10M Find files larger than 10MB find . -mtime -7 Files modified in last 7 days which python Find where a command lives locate file.txt Fast file search (uses index) Pipes and Redirection # Pipe — send output of one command to another ls -la | grep ".txt" cat file.txt | sort | uniq ps aux | grep node # Redirect output to file echo "hello" > file.txt # overwrite echo "world" >> file.txt # append # Redirect errors command 2> errors.log # stderr to file command > output.log 2>&1 # stdout + stderr to file command &> all.log # same (bash shorthand) # /dev/null — discard output command > /dev/null 2>&1 # silence everything cmd1 | cmd2 pipe: stdout of cmd1 → stdin of cmd2 cmd > file redirect stdout to file (overwrite) cmd >> file redirect stdout to file (append) cmd 2> file redirect stderr to file cmd < file use file as stdin Permissions -rwxr-xr-- 1 alex staff 4096 Mar 15 file.txt │├─┤├─┤├─┤ │ │ │ │ │ │ │ └── Others: read only │ │ └───── Group: read + execute │ └───────── Owner: read + write + execute └─────────── File type (- = file, d = directory, l = link) Command Description chmod 755 file rwxr-xr-x (owner all, group/others read+exec) chmod 644 file rw-r–r– (owner read+write, others read) chmod +x script.sh Add execute permission chmod -w file.txt Remove write permission chown user:group file Change owner and group chown -R user dir/ Change owner recursively Number Permission 7 rwx (read + write + execute) 6 rw- (read + write) 5 r-x (read + execute) 4 r– (read only) 0 — (no permission) Processes Command Description ps aux List all processes ps aux | grep node Find a specific process top Live process monitor htop Better process monitor (install separately) kill <pid> Send SIGTERM (graceful stop) kill -9 <pid> Send SIGKILL (force stop) killall node Kill all processes by name lsof -i :3000 Find what is using port 3000 jobs List background jobs bg Resume job in background fg Bring job to foreground command & Run command in background nohup command & Run and keep running after logout Disk and System Command Description df -h Disk usage (human-readable) du -sh dir/ Directory size du -sh * | sort -rh Largest items in current dir free -h Memory usage (Linux) uname -a System info hostname Machine name uptime System uptime and load date Current date/time cal Calendar Networking Command Description curl https://example.com Fetch a URL curl -o file.html https://example.com Download to file curl -X POST -d '{"key":"value"}' -H "Content-Type: application/json" url POST JSON wget https://example.com/file.zip Download a file ping example.com Test connectivity ifconfig / ip addr Show network interfaces netstat -tlnp / ss -tlnp Show listening ports ssh user@host Connect via SSH scp file.txt user@host:/path Copy file to remote scp user@host:/path/file.txt . Copy file from remote Archives Command Description tar -czf archive.tar.gz dir/ Create gzip archive tar -xzf archive.tar.gz Extract gzip archive tar -xzf archive.tar.gz -C /dest Extract to directory zip -r archive.zip dir/ Create zip archive unzip archive.zip Extract zip Text Processing # Sort lines sort file.txt sort -r file.txt # reverse sort -n file.txt # numeric sort sort -u file.txt # unique only # Unique lines (file must be sorted first) sort file.txt | uniq sort file.txt | uniq -c # count occurrences # Cut columns cut -d',' -f1,3 data.csv # columns 1 and 3 (comma delimiter) # Replace text sed 's/old/new/g' file.txt # replace all occurrences sed -i 's/old/new/g' file.txt # in-place edit (Linux) sed -i '' 's/old/new/g' file.txt # in-place edit (macOS — needs empty '') # Print specific lines awk '{print $1, $3}' file.txt # columns 1 and 3 (space delimiter) awk -F',' '{print $1}' data.csv # with custom delimiter # Count and summarize cat access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head -10 # → top 10 IP addresses in an access log Keyboard Shortcuts (Terminal) Shortcut Action Ctrl+C Cancel current command Ctrl+D Exit shell / close terminal Ctrl+Z Suspend current process Ctrl+L Clear screen Ctrl+R Search command history Ctrl+A Move cursor to start of line Ctrl+E Move cursor to end of line Ctrl+W Delete word before cursor Ctrl+U Delete entire line before cursor Tab Auto-complete file/command name !! Repeat last command !$ Last argument of previous command Environment Variables echo $HOME # print variable export MY_VAR="value" # set for current session echo 'export MY_VAR="value"' >> ~/.zshrc # permanent (Zsh) echo 'export MY_VAR="value"' >> ~/.bashrc # permanent (Bash) env # list all env variables printenv PATH # print specific variable Common Mistakes rm -rf / or rm -rf * — There is no recycle bin in the terminal. Deleted files are gone. Always double-check your path before running rm -rf. Use ls first to preview what will be deleted. ...

July 14, 2026 · 6 min

Rust Cheat Sheet 2026 — Syntax, Ownership, and Common Patterns

Bookmark this page. Use Ctrl+F (or Cmd+F on Mac) to find what you need. This cheat sheet covers Rust syntax, ownership, traits, error handling, and common patterns. Try examples at play.rust-lang.org. Last updated: March 2026 Variables and Types let name = "Alex"; // immutable (default) let mut count = 0; // mutable let age: i32 = 25; // explicit type const MAX: u32 = 100; // compile-time constant Type Description Example i8, i16, i32, i64, i128 Signed integers let x: i32 = -42; u8, u16, u32, u64, u128 Unsigned integers let x: u32 = 42; f32, f64 Floating point let x: f64 = 3.14; bool Boolean true, false char Unicode character 'A', '🚀' &str String slice (borrowed) "hello" String Owned string (heap) String::from("hello") () Unit type (void) fn do_thing() { } [T; N] Fixed array [1, 2, 3] Vec<T> Dynamic array vec![1, 2, 3] (T, U) Tuple (42, "hello") Option<T> Nullable value Some(42) or None Result<T, E> Success or error Ok(42) or Err("fail") Type Conversions let x: i32 = 42; let y: f64 = x as f64; // 42.0 let s: String = x.to_string(); // "42" let n: i32 = "42".parse().unwrap(); // 42 let n: i32 = "42".parse().unwrap_or(0); // 42 (or 0 on error) Strings // &str — string slice, borrowed, immutable let greeting: &str = "hello"; // String — owned, heap-allocated, growable let mut name = String::from("Alex"); name.push_str(" Smith"); // append name.push('!'); // append char // Conversions let s: String = "hello".to_string(); let s: &str = &name; // String → &str (auto-deref) // Common methods name.len() // byte length name.is_empty() // true if empty name.contains("Alex") // substring check name.starts_with("A") name.to_uppercase() name.to_lowercase() name.trim() // remove whitespace name.replace("Alex", "Sam") name.split(" ") // iterator of parts // Format let msg = format!("Hello {name}, age {age}"); Ownership and Borrowing // Ownership — each value has one owner let s1 = String::from("hello"); let s2 = s1; // s1 is MOVED to s2 // println!("{s1}"); // ERROR: s1 no longer valid // Clone — deep copy let s1 = String::from("hello"); let s2 = s1.clone(); // both valid // Borrowing — reference without taking ownership fn print_len(s: &String) { // immutable borrow println!("{}", s.len()); } fn add_excl(s: &mut String) { // mutable borrow s.push('!'); } // Rules: // 1. Many immutable references (&T) OR one mutable reference (&mut T) // 2. References must always be valid (no dangling) Structs struct User { name: String, age: u32, active: bool, } let user = User { name: String::from("Alex"), age: 25, active: true, }; // Access println!("{}", user.name); // Update syntax let user2 = User { age: 26, ..user }; // Tuple struct struct Point(f64, f64); let p = Point(1.0, 2.0); // Methods impl User { // Constructor (convention) fn new(name: &str, age: u32) -> Self { Self { name: name.to_string(), age, active: true } } // Method (takes &self) fn greet(&self) -> String { format!("Hi, I'm {}", self.name) } } Enums and Pattern Matching enum Direction { North, South, East, West } // Enums with data enum Shape { Circle(f64), // radius Rectangle(f64, f64), // width, height Triangle { base: f64, height: f64 }, } // Pattern matching with match match shape { Shape::Circle(r) => std::f64::consts::PI * r * r, Shape::Rectangle(w, h) => w * h, Shape::Triangle { base, height } => 0.5 * base * height, } // if let — match a single pattern if let Some(value) = optional { println!("Got: {value}"); } // let else — match or diverge let Some(value) = optional else { return; }; Option and Result // Option<T> — value or nothing let name: Option<&str> = Some("Alex"); let empty: Option<&str> = None; name.unwrap() // "Alex" (panics if None) name.unwrap_or("Unknown") // "Alex" (or default) name.is_some() // true name.is_none() // false name.map(|n| n.len()) // Some(4) // Result<T, E> — success or error fn divide(a: f64, b: f64) -> Result<f64, String> { if b == 0.0 { Err("division by zero".to_string()) } else { Ok(a / b) } } // ? operator — propagate errors fn read_file(path: &str) -> Result<String, std::io::Error> { let content = std::fs::read_to_string(path)?; // returns Err early Ok(content) } // Handle Result match divide(10.0, 3.0) { Ok(result) => println!("{result}"), Err(e) => println!("Error: {e}"), } Traits // Define a trait trait Greet { fn greet(&self) -> String; // Default implementation fn hello(&self) -> String { format!("Hello from {}", self.greet()) } } // Implement for a type impl Greet for User { fn greet(&self) -> String { self.name.clone() } } // Trait as parameter fn print_greeting(item: &impl Greet) { println!("{}", item.greet()); } // Trait bound syntax fn print_greeting<T: Greet>(item: &T) { println!("{}", item.greet()); } // Common derive traits #[derive(Debug, Clone, PartialEq, Eq, Hash)] struct Point { x: i32, y: i32 } Collections // Vec — dynamic array let mut v = vec![1, 2, 3]; v.push(4); v.pop(); // Some(4) v.len(); // 3 v[0]; // 1 (panics if out of bounds) v.get(0); // Some(&1) (safe) v.contains(&2); // true v.iter().filter(|&&x| x > 1).collect::<Vec<_>>(); // HashMap use std::collections::HashMap; let mut map = HashMap::new(); map.insert("name", "Alex"); map.get("name"); // Some(&"Alex") map.contains_key("name"); // true map.entry("age").or_insert("25"); for (key, value) in &map { } // HashSet use std::collections::HashSet; let mut set = HashSet::new(); set.insert(1); set.contains(&1); // true Iterators let nums = vec![1, 2, 3, 4, 5]; nums.iter().map(|x| x * 2).collect::<Vec<_>>(); // [2, 4, 6, 8, 10] nums.iter().filter(|&&x| x > 2).collect::<Vec<_>>(); // [3, 4, 5] nums.iter().sum::<i32>(); // 15 nums.iter().any(|&x| x > 3); // true nums.iter().all(|&x| x > 0); // true nums.iter().find(|&&x| x > 3); // Some(&4) nums.iter().position(|&x| x == 3); // Some(2) nums.iter().enumerate(); // (index, &value) nums.iter().zip(other.iter()); // pair elements nums.iter().take(3).collect::<Vec<_>>(); // [1, 2, 3] nums.iter().skip(2).collect::<Vec<_>>(); // [3, 4, 5] nums.iter().flat_map(|x| vec![x, x * 10]); nums.iter().fold(0, |acc, &x| acc + x); // 15 Control Flow // if / else (is an expression) let status = if age >= 18 { "adult" } else { "minor" }; // loop (infinite, break with value) let result = loop { if condition { break 42; } }; // while while count > 0 { count -= 1; } // for for i in 0..5 { } // 0, 1, 2, 3, 4 for i in 0..=5 { } // 0, 1, 2, 3, 4, 5 for item in &vec { } // iterate by reference for item in vec { } // iterate by value (moves) Closures let add = |a: i32, b: i32| -> i32 { a + b }; let double = |x| x * 2; // types inferred let greet = || println!("Hello"); // Closures capture variables let name = String::from("Alex"); let greet = || println!("Hello {name}"); // borrows name let greet = move || println!("Hello {name}"); // takes ownership Cargo Commands Command Description cargo new my_app Create a new project cargo run Build and run cargo build Build (debug) cargo build --release Build (optimized) cargo test Run tests cargo check Fast compile check (no binary) cargo clippy Lint your code cargo fmt Auto-format code cargo add serde Add a dependency cargo doc --open Generate and open docs Lifetimes // Lifetimes tell the compiler how long references are valid fn longest<'a>(a: &'a str, b: &'a str) -> &'a str { if a.len() > b.len() { a } else { b } } // Struct with a reference needs a lifetime struct Excerpt<'a> { text: &'a str, } Rule: if a function returns a reference, it must come from one of the inputs (annotated with the same lifetime). ...

July 13, 2026 · 7 min

Kubernetes Tutorial #4: ConfigMaps and Secrets — Manage Configuration

Your application needs a database host, a port number, an API key, and a password. How do you pass these to your containers in Kubernetes? You should not hardcode them in the Docker image. You should not put passwords in your Deployment YAML either. Kubernetes has two objects for this: ConfigMap — for non-sensitive configuration Secret — for sensitive data like passwords and API keys Why Not Hardcode Config? Imagine you hardcode a database hostname in your Docker image: ...

July 13, 2026 · 6 min

Kubernetes Tutorial #3: Pods, Deployments, and Services

When you deploy an application to Kubernetes, you work with three objects almost every time: Pods, Deployments, and Services. A Pod is the unit that runs your containers A Deployment manages a set of Pods and handles updates A Service gives your Pods a stable network address Understanding these three is the foundation for everything else in Kubernetes. Prerequisites: A running Kubernetes cluster. See Kubernetes Tutorial #2: Installing Kubernetes Locally. Pods — The Smallest Unit A Pod is the smallest deployable unit in Kubernetes. It wraps one or more containers that share the same network and storage. ...

July 12, 2026 · 7 min

Kubernetes Tutorial #2: Installing Kubernetes Locally (minikube + kind)

You cannot learn Kubernetes from docs alone. You need a real cluster to practice on. The good news: you can run a full Kubernetes cluster on your laptop for free. In this tutorial, you will install Kubernetes locally, run your first pod, and learn the essential kubectl commands. Prerequisites: Docker must be installed. If not, see Docker Tutorial #2: How to Install Docker. Options for Running Kubernetes Locally There are four main ways to run Kubernetes on your laptop: ...

July 11, 2026 · 6 min