<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: opscanopy</title>
    <description>The latest articles on DEV Community by opscanopy (@opscanopy).</description>
    <link>https://dev.to/opscanopy</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3982315%2F76cf6c25-3b03-4a82-9a14-c0f39bab7ea1.png</url>
      <title>DEV Community: opscanopy</title>
      <link>https://dev.to/opscanopy</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/opscanopy"/>
    <language>en</language>
    <item>
      <title>Networking for DevOps Engineers: The Complete Guide</title>
      <dc:creator>opscanopy</dc:creator>
      <pubDate>Fri, 18 Sep 2026 16:19:29 +0000</pubDate>
      <link>https://dev.to/opscanopy/networking-for-devops-engineers-the-complete-guide-j9o</link>
      <guid>https://dev.to/opscanopy/networking-for-devops-engineers-the-complete-guide-j9o</guid>
      <description>&lt;p&gt;Networking is the invisible plumbing that every deployment depends on. When a pod can't reach its database, a deploy fails, or latency spikes with no obvious cause, the engineer who understands the network stack finds the answer while everyone else is still guessing. This guide covers the networking fundamentals every DevOps practitioner needs — from binary subnet math to Kubernetes service meshes — grounded in the commands you'll actually run. If you want a structured learning path alongside this reference, see the &lt;a href="https://opscanopy.com/learn/roadmaps/networking/" rel="noopener noreferrer"&gt;Networking roadmap&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Networking Matters for DevOps
&lt;/h2&gt;

&lt;p&gt;Infrastructure work is networking work, whether you recognize it or not. Every CI/CD pipeline fetches artifacts over the wire. Every container runtime assigns IP addresses, manages virtual interfaces, and enforces iptables rules. Cloud platform mistakes — wrong CIDR range, missing security group rule, overlooked NAT gateway — routinely stall production deployments and generate hours of troubleshooting.&lt;/p&gt;

&lt;p&gt;Concrete scenarios where networking knowledge pays off immediately:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Debugging why two services in the same VPC can't communicate (security group rule, route table missing, NACL block, wrong subnet)&lt;/li&gt;
&lt;li&gt;Sizing a VPC subnet large enough to accommodate auto-scaling groups that expand to hundreds of pods&lt;/li&gt;
&lt;li&gt;Investigating certificate errors, TLS handshake failures, or mixed-content warnings in HTTPS apps&lt;/li&gt;
&lt;li&gt;Tuning load balancer health-check thresholds and connection-draining windows to achieve zero-downtime deploys&lt;/li&gt;
&lt;li&gt;Reading &lt;code&gt;tcpdump&lt;/code&gt; or &lt;code&gt;Wireshark&lt;/code&gt; output to understand exactly what traffic crossed the wire during an incident&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The sections below build from the ground up: models → addresses → subnets → protocols → services → tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  The OSI and TCP/IP Models
&lt;/h2&gt;

&lt;p&gt;Two reference models describe how data travels from application to wire. The OSI (Open Systems Interconnection) model has seven layers; the TCP/IP model collapses those into four. Both remain useful — OSI gives precise vocabulary for troubleshooting, TCP/IP maps to real protocol implementations.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;OSI Layer&lt;/th&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;TCP/IP Layer&lt;/th&gt;
&lt;th&gt;Examples&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;Application&lt;/td&gt;
&lt;td&gt;Application&lt;/td&gt;
&lt;td&gt;HTTP, HTTPS, DNS, SMTP, FTP, SSH&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Presentation&lt;/td&gt;
&lt;td&gt;Application&lt;/td&gt;
&lt;td&gt;TLS/SSL, MIME encoding&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Session&lt;/td&gt;
&lt;td&gt;Application&lt;/td&gt;
&lt;td&gt;TLS sessions, RPC, NetBIOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Transport&lt;/td&gt;
&lt;td&gt;Transport&lt;/td&gt;
&lt;td&gt;TCP, UDP, SCTP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Network&lt;/td&gt;
&lt;td&gt;Internet&lt;/td&gt;
&lt;td&gt;IPv4, IPv6, ICMP, IPsec&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Data Link&lt;/td&gt;
&lt;td&gt;Network Access&lt;/td&gt;
&lt;td&gt;Ethernet, Wi-Fi (802.11), ARP, VLAN&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Physical&lt;/td&gt;
&lt;td&gt;Network Access&lt;/td&gt;
&lt;td&gt;Cable, fiber, radio, transceivers&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;br&gt;
&lt;/p&gt;
OSI's seven layers collapse into four TCP/IP layers; each arrow shows which OSI layers map to the corresponding TCP/IP layer.



&lt;p&gt;&lt;strong&gt;Encapsulation&lt;/strong&gt; moves data down the stack on the sender and up on the receiver. Each layer wraps the payload with its own header (and sometimes trailer). By the time an HTTP request leaves a NIC, the original bytes are nested inside: HTTP → TCP segment → IP packet → Ethernet frame.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where DevOps tooling sits:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Load balancers operate at L4 (TCP/UDP) or L7 (HTTP/HTTPS)&lt;/li&gt;
&lt;li&gt;Firewalls and security groups filter at L3/L4 (IP addresses, ports, protocol)&lt;/li&gt;
&lt;li&gt;Service meshes (Envoy, Linkerd) intercept at L4/L7 inside the application network&lt;/li&gt;
&lt;li&gt;Overlay networks (VXLAN, Geneve) wrap L2 frames inside L3 packets — how Kubernetes CNI plugins create virtual pod networks across physical hosts&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; When troubleshooting, identify which layer the failure lives in. "Can't reach the server" is vague. "The TCP SYN goes out but no SYN-ACK returns" pins the problem to L3/L4, narrowing the suspect list to routes, security groups, and firewalls.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  IP Addressing
&lt;/h2&gt;

&lt;h3&gt;
  
  
  IPv4 Structure
&lt;/h3&gt;

&lt;p&gt;An IPv4 address is 32 bits written as four decimal octets separated by dots: &lt;code&gt;192.168.1.100&lt;/code&gt;. Each octet ranges from 0–255 (2⁸ possible values). The address space is divided into a &lt;strong&gt;network portion&lt;/strong&gt; (identifies the subnet) and a &lt;strong&gt;host portion&lt;/strong&gt; (identifies the device within that subnet). The split point is determined by the subnet mask.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Inspect assigned IPs on Linux&lt;/span&gt;
ip addr show

&lt;span class="c"&gt;# Legacy command (still common on older systems)&lt;/span&gt;
ifconfig &lt;span class="nt"&gt;-a&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Public vs Private Address Ranges
&lt;/h3&gt;

&lt;p&gt;RFC 1918 reserves three blocks for private use — they are not routable on the public internet:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Range&lt;/th&gt;
&lt;th&gt;CIDR&lt;/th&gt;
&lt;th&gt;Size&lt;/th&gt;
&lt;th&gt;Common Use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;10.0.0.0 – 10.255.255.255&lt;/td&gt;
&lt;td&gt;10.0.0.0/8&lt;/td&gt;
&lt;td&gt;~16.7 million&lt;/td&gt;
&lt;td&gt;Enterprise, cloud VPCs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;172.16.0.0 – 172.31.255.255&lt;/td&gt;
&lt;td&gt;172.16.0.0/12&lt;/td&gt;
&lt;td&gt;~1 million&lt;/td&gt;
&lt;td&gt;Mid-size networks, Docker default&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;192.168.0.0 – 192.168.255.255&lt;/td&gt;
&lt;td&gt;192.168.0.0/16&lt;/td&gt;
&lt;td&gt;~65,000&lt;/td&gt;
&lt;td&gt;Home, small office&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Additional special-purpose ranges:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;127.0.0.0/8&lt;/code&gt; — loopback (localhost); &lt;code&gt;127.0.0.1&lt;/code&gt; is the standard loopback address&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;169.254.0.0/16&lt;/code&gt; — link-local; assigned automatically when DHCP fails (APIPA)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;100.64.0.0/10&lt;/code&gt; — shared address space (ISP carrier-grade NAT, RFC 6598)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;0.0.0.0/0&lt;/code&gt; — the default route, meaning "everything"&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  A Note on IPv6
&lt;/h3&gt;

&lt;p&gt;IPv6 uses 128-bit addresses written in eight groups of four hex digits, e.g. &lt;code&gt;2001:0db8:85a3:0000:0000:8a2e:0370:7334&lt;/code&gt;. Consecutive groups of zeroes can be collapsed with &lt;code&gt;::&lt;/code&gt; — the above becomes &lt;code&gt;2001:db8:85a3::8a2e:370:7334&lt;/code&gt;. The primary motivation was exhaustion of the IPv4 space.&lt;/p&gt;

&lt;p&gt;Key DevOps IPv6 touchpoints: AWS dual-stack VPCs, Kubernetes Pod IPs (IPv6 mode), and modern load balancers that accept both stacks. The subnet math uses /64 prefixes for most LANs (leaving 64 bits for host IDs).&lt;/p&gt;

&lt;p&gt;Use the &lt;a href="https://opscanopy.com/ip-address-converter/" rel="noopener noreferrer"&gt;IP Address Converter&lt;/a&gt; to convert between dotted-decimal, binary, and hex representations — indispensable when verifying subnet masks or reading raw packet captures.&lt;/p&gt;

&lt;h2&gt;
  
  
  Subnets and CIDR
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Subnet Masks and Prefix Length
&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;subnet mask&lt;/strong&gt; is a 32-bit value with all network bits set to 1 and all host bits set to 0. It tells a device which part of an IP address identifies the network.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CIDR (Classless Inter-Domain Routing)&lt;/strong&gt; notation expresses the same information as a prefix length after a slash: &lt;code&gt;192.168.1.0/24&lt;/code&gt; means the first 24 bits are the network, the remaining 8 bits are for hosts.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;CIDR Prefix&lt;/th&gt;
&lt;th&gt;Subnet Mask&lt;/th&gt;
&lt;th&gt;# Addresses&lt;/th&gt;
&lt;th&gt;Usable Hosts&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;/8&lt;/td&gt;
&lt;td&gt;255.0.0.0&lt;/td&gt;
&lt;td&gt;16,777,216&lt;/td&gt;
&lt;td&gt;16,777,214&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/16&lt;/td&gt;
&lt;td&gt;255.255.0.0&lt;/td&gt;
&lt;td&gt;65,536&lt;/td&gt;
&lt;td&gt;65,534&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/20&lt;/td&gt;
&lt;td&gt;255.255.240.0&lt;/td&gt;
&lt;td&gt;4,096&lt;/td&gt;
&lt;td&gt;4,094&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/24&lt;/td&gt;
&lt;td&gt;255.255.255.0&lt;/td&gt;
&lt;td&gt;256&lt;/td&gt;
&lt;td&gt;254&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/25&lt;/td&gt;
&lt;td&gt;255.255.255.128&lt;/td&gt;
&lt;td&gt;128&lt;/td&gt;
&lt;td&gt;126&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/26&lt;/td&gt;
&lt;td&gt;255.255.255.192&lt;/td&gt;
&lt;td&gt;64&lt;/td&gt;
&lt;td&gt;62&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/27&lt;/td&gt;
&lt;td&gt;255.255.255.224&lt;/td&gt;
&lt;td&gt;32&lt;/td&gt;
&lt;td&gt;30&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/28&lt;/td&gt;
&lt;td&gt;255.255.255.240&lt;/td&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/29&lt;/td&gt;
&lt;td&gt;255.255.255.248&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/30&lt;/td&gt;
&lt;td&gt;255.255.255.252&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/32&lt;/td&gt;
&lt;td&gt;255.255.255.255&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;host route only&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;
A /24 block dedicates 24 bits to the network and 8 bits to hosts, yielding 256 total addresses with 254 usable after reserving the network and broadcast addresses.





&lt;h3&gt;
  
  
  The Host Count Formula
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;host_bits = 32 − prefix_length
total_addresses = 2^host_bits
usable_hosts = 2^host_bits − 2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Subtract 2 because the first address is the &lt;strong&gt;network address&lt;/strong&gt; (all host bits zero) and the last is the &lt;strong&gt;broadcast address&lt;/strong&gt; (all host bits one). Neither can be assigned to a host.&lt;/p&gt;

&lt;h3&gt;
  
  
  Worked Example: Subnetting 10.0.0.0/16
&lt;/h3&gt;

&lt;p&gt;You need to allocate subnets inside a VPC at &lt;code&gt;10.0.0.0/16&lt;/code&gt;. You want:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One public subnet per availability zone (3 AZs, ~250 hosts each)&lt;/li&gt;
&lt;li&gt;One private subnet per AZ (~500 hosts each)&lt;/li&gt;
&lt;li&gt;One database subnet per AZ (~50 hosts each)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Step 1 — Choose prefix lengths:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Public subnets: /24 → 254 usable hosts ✓&lt;/li&gt;
&lt;li&gt;Private subnets: /23 → 510 usable hosts ✓&lt;/li&gt;
&lt;li&gt;Database subnets: /26 → 62 usable hosts ✓&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Step 2 — Lay out the ranges (keeping subnets non-overlapping):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Subnet&lt;/th&gt;
&lt;th&gt;CIDR&lt;/th&gt;
&lt;th&gt;Usable Range&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Public AZ-a&lt;/td&gt;
&lt;td&gt;10.0.0.0/24&lt;/td&gt;
&lt;td&gt;10.0.0.1–10.0.0.254&lt;/td&gt;
&lt;td&gt;Public subnet, AZ us-east-1a&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Public AZ-b&lt;/td&gt;
&lt;td&gt;10.0.1.0/24&lt;/td&gt;
&lt;td&gt;10.0.1.1–10.0.1.254&lt;/td&gt;
&lt;td&gt;Public subnet, AZ us-east-1b&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Public AZ-c&lt;/td&gt;
&lt;td&gt;10.0.2.0/24&lt;/td&gt;
&lt;td&gt;10.0.2.1–10.0.2.254&lt;/td&gt;
&lt;td&gt;Public subnet, AZ us-east-1c&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Private AZ-a&lt;/td&gt;
&lt;td&gt;10.0.4.0/23&lt;/td&gt;
&lt;td&gt;10.0.4.1–10.0.5.254&lt;/td&gt;
&lt;td&gt;Private subnet, AZ us-east-1a&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Private AZ-b&lt;/td&gt;
&lt;td&gt;10.0.6.0/23&lt;/td&gt;
&lt;td&gt;10.0.6.1–10.0.7.254&lt;/td&gt;
&lt;td&gt;Private subnet, AZ us-east-1b&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Private AZ-c&lt;/td&gt;
&lt;td&gt;10.0.8.0/23&lt;/td&gt;
&lt;td&gt;10.0.8.1–10.0.9.254&lt;/td&gt;
&lt;td&gt;Private subnet, AZ us-east-1c&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DB AZ-a&lt;/td&gt;
&lt;td&gt;10.0.16.0/26&lt;/td&gt;
&lt;td&gt;10.0.16.1–10.0.16.62&lt;/td&gt;
&lt;td&gt;Database subnet, AZ us-east-1a&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DB AZ-b&lt;/td&gt;
&lt;td&gt;10.0.16.64/26&lt;/td&gt;
&lt;td&gt;10.0.16.65–10.0.16.126&lt;/td&gt;
&lt;td&gt;Database subnet, AZ us-east-1b&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DB AZ-c&lt;/td&gt;
&lt;td&gt;10.0.16.128/26&lt;/td&gt;
&lt;td&gt;10.0.16.129–10.0.16.190&lt;/td&gt;
&lt;td&gt;Database subnet, AZ us-east-1c&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Cloud providers reserve additional addresses inside each subnet. AWS, for example, reserves the first 4 addresses and the last 1 in every subnet, reducing usable hosts by 5.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  VLSM (Variable Length Subnet Masking)
&lt;/h3&gt;

&lt;p&gt;VLSM allows different subnets within the same address space to use different prefix lengths — exactly the approach used in the example above. Each subnet is sized to its actual requirement, minimizing waste. The rule is that subnets must not overlap, and each subnet's network address must be aligned to its block size (a /23 must start on an even /24 boundary, a /26 must start at a multiple of 64, etc.).&lt;/p&gt;

&lt;h3&gt;
  
  
  Free Subnet Tools
&lt;/h3&gt;

&lt;p&gt;You should never do this math by hand under pressure. Use these tools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://opscanopy.com/subnet-calculator/" rel="noopener noreferrer"&gt;Subnet Calculator&lt;/a&gt;&lt;/strong&gt; — enter any IP/prefix and instantly get network address, broadcast, usable host range, and mask&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://opscanopy.com/cidr-checker/" rel="noopener noreferrer"&gt;CIDR Checker&lt;/a&gt;&lt;/strong&gt; — verify whether an IP falls inside a given CIDR block, check overlap between ranges&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://opscanopy.com/subnet-splitter/" rel="noopener noreferrer"&gt;Subnet Splitter&lt;/a&gt;&lt;/strong&gt; — divide a parent CIDR into equal or custom-sized child subnets automatically&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  TCP vs UDP
&lt;/h2&gt;

&lt;p&gt;Both TCP and UDP sit at Layer 4 (Transport). The choice between them is a design trade-off between reliability and speed.&lt;/p&gt;

&lt;h3&gt;
  
  
  TCP — Transmission Control Protocol
&lt;/h3&gt;

&lt;p&gt;TCP is &lt;strong&gt;connection-oriented&lt;/strong&gt;: before any data flows, a three-way handshake establishes the connection.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client → Server   SYN       (sequence number X)
Server → Client   SYN-ACK   (sequence number Y, ack X+1)
Client → Server   ACK       (ack Y+1)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;/p&gt;
The TCP three-way handshake: client sends SYN, server replies with SYN-ACK, client confirms with ACK, then bidirectional data flow begins.





&lt;p&gt;After the handshake, TCP guarantees:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ordered delivery&lt;/strong&gt; — segments are reassembled in sequence regardless of arrival order&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reliable delivery&lt;/strong&gt; — lost segments are detected (via ACK timeout or duplicate ACKs) and retransmitted&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flow control&lt;/strong&gt; — receiver advertises a window size to prevent buffer overflow&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Congestion control&lt;/strong&gt; — sender reduces rate when the network signals congestion (CUBIC, BBR, etc.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Connection teardown uses a four-way FIN/FIN-ACK/FIN/FIN-ACK exchange, leaving connections in &lt;code&gt;TIME_WAIT&lt;/code&gt; for 2×MSL (typically 60–120 seconds) to absorb delayed duplicates.&lt;/p&gt;

&lt;h3&gt;
  
  
  UDP — User Datagram Protocol
&lt;/h3&gt;

&lt;p&gt;UDP is &lt;strong&gt;connectionless&lt;/strong&gt;: datagrams are fired without a handshake. There is no delivery guarantee, no ordering, and no retransmission. The sender does not know whether the packet arrived.&lt;/p&gt;

&lt;p&gt;The upside is minimal overhead and latency. For workloads that handle their own reliability, or where a slightly stale/lost packet is acceptable, UDP wins.&lt;/p&gt;

&lt;h3&gt;
  
  
  When to Use Each
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Characteristic&lt;/th&gt;
&lt;th&gt;TCP&lt;/th&gt;
&lt;th&gt;UDP&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Delivery guarantee&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ordering&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Connection state&lt;/td&gt;
&lt;td&gt;Yes (SYN/FIN)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Overhead&lt;/td&gt;
&lt;td&gt;Higher&lt;/td&gt;
&lt;td&gt;Lower&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Typical latency&lt;/td&gt;
&lt;td&gt;Higher&lt;/td&gt;
&lt;td&gt;Lower&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Use cases&lt;/td&gt;
&lt;td&gt;HTTP/S, SSH, databases, file transfer&lt;/td&gt;
&lt;td&gt;DNS, DHCP, VoIP, video streaming, gaming, QUIC/HTTP3&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; QUIC (the underlying protocol for HTTP/3) runs over UDP but implements its own reliability, ordering, and congestion control at the application level — combining UDP's flexibility with TCP-like guarantees while eliminating head-of-line blocking.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Inspecting TCP State
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Show all listening and established TCP connections&lt;/span&gt;
ss &lt;span class="nt"&gt;-tnp&lt;/span&gt;

&lt;span class="c"&gt;# Show UDP sockets&lt;/span&gt;
ss &lt;span class="nt"&gt;-unp&lt;/span&gt;

&lt;span class="c"&gt;# Filter for a specific port&lt;/span&gt;
ss &lt;span class="nt"&gt;-tnp&lt;/span&gt; sport &lt;span class="o"&gt;=&lt;/span&gt; :443

&lt;span class="c"&gt;# Count connections by state&lt;/span&gt;
ss &lt;span class="nt"&gt;-tan&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'NR&amp;gt;1 {print $1}'&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; | &lt;span class="nb"&gt;uniq&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-rn&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Ports and Sockets
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What Is a Socket?
&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;socket&lt;/strong&gt; is the combination of IP address and port that uniquely identifies one end of a network connection. A full TCP connection is a 4-tuple: &lt;code&gt;(src IP, src port, dst IP, dst port)&lt;/code&gt;. This allows a single server IP to handle thousands of simultaneous connections to the same port — each connection's tuple is unique.&lt;/p&gt;

&lt;h3&gt;
  
  
  Well-Known Ports (0–1023)
&lt;/h3&gt;

&lt;p&gt;These ports are assigned by IANA and typically require root/admin privileges to bind.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Port&lt;/th&gt;
&lt;th&gt;Protocol&lt;/th&gt;
&lt;th&gt;Service&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;20/21&lt;/td&gt;
&lt;td&gt;TCP&lt;/td&gt;
&lt;td&gt;FTP (data/control)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;22&lt;/td&gt;
&lt;td&gt;TCP&lt;/td&gt;
&lt;td&gt;SSH&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;23&lt;/td&gt;
&lt;td&gt;TCP&lt;/td&gt;
&lt;td&gt;Telnet (insecure, avoid)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;25&lt;/td&gt;
&lt;td&gt;TCP&lt;/td&gt;
&lt;td&gt;SMTP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;53&lt;/td&gt;
&lt;td&gt;TCP/UDP&lt;/td&gt;
&lt;td&gt;DNS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;67/68&lt;/td&gt;
&lt;td&gt;UDP&lt;/td&gt;
&lt;td&gt;DHCP (server/client)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;80&lt;/td&gt;
&lt;td&gt;TCP&lt;/td&gt;
&lt;td&gt;HTTP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;110&lt;/td&gt;
&lt;td&gt;TCP&lt;/td&gt;
&lt;td&gt;POP3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;123&lt;/td&gt;
&lt;td&gt;UDP&lt;/td&gt;
&lt;td&gt;NTP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;143&lt;/td&gt;
&lt;td&gt;TCP&lt;/td&gt;
&lt;td&gt;IMAP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;443&lt;/td&gt;
&lt;td&gt;TCP&lt;/td&gt;
&lt;td&gt;HTTPS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;445&lt;/td&gt;
&lt;td&gt;TCP&lt;/td&gt;
&lt;td&gt;SMB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;514&lt;/td&gt;
&lt;td&gt;UDP&lt;/td&gt;
&lt;td&gt;Syslog&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;587&lt;/td&gt;
&lt;td&gt;TCP&lt;/td&gt;
&lt;td&gt;SMTP submission (with auth)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;993&lt;/td&gt;
&lt;td&gt;TCP&lt;/td&gt;
&lt;td&gt;IMAPS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;995&lt;/td&gt;
&lt;td&gt;TCP&lt;/td&gt;
&lt;td&gt;POP3S&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Registered and Ephemeral Ports
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Registered (1024–49151):&lt;/strong&gt; Assigned to specific services by IANA but do not require root. Common examples: 3306 (MySQL), 5432 (PostgreSQL), 6379 (Redis), 8080 (HTTP alternate), 9200 (Elasticsearch), 27017 (MongoDB).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ephemeral (49152–65535):&lt;/strong&gt; Assigned by the OS to the client side of outgoing connections. The range is configurable; Linux defaults to roughly 32768–60999 (check &lt;code&gt;/proc/sys/net/ipv4/ip_local_port_range&lt;/code&gt;).
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check the ephemeral port range on Linux&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; /proc/sys/net/ipv4/ip_local_port_range

&lt;span class="c"&gt;# See what process owns a specific port&lt;/span&gt;
ss &lt;span class="nt"&gt;-tnlp&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; :8080

&lt;span class="c"&gt;# Check if a port is open from inside the host&lt;/span&gt;
nc &lt;span class="nt"&gt;-zv&lt;/span&gt; 127.0.0.1 5432
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; Running out of ephemeral ports under high outbound connection rates (e.g., a service hammering a downstream API) causes connection failures. Monitor with &lt;code&gt;ss -s&lt;/code&gt; and widen the range if needed: &lt;code&gt;sysctl -w net.ipv4.ip_local_port_range="1024 65535"&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  DNS
&lt;/h2&gt;

&lt;p&gt;The Domain Name System translates human-readable names into IP addresses. From a DevOps perspective, DNS is a source of subtle bugs, slow failovers, and cache-related incidents.&lt;/p&gt;

&lt;h3&gt;
  
  
  Resolution Flow
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Client queries its &lt;strong&gt;stub resolver&lt;/strong&gt; (the OS resolver, configured via &lt;code&gt;/etc/resolv.conf&lt;/code&gt; or systemd-resolved)&lt;/li&gt;
&lt;li&gt;Stub resolver forwards to a &lt;strong&gt;recursive resolver&lt;/strong&gt; (e.g., &lt;code&gt;8.8.8.8&lt;/code&gt;, your VPC's &lt;code&gt;.2&lt;/code&gt; resolver)&lt;/li&gt;
&lt;li&gt;Recursive resolver checks its cache; on a miss it begins iterative resolution:

&lt;ul&gt;
&lt;li&gt;Queries a &lt;strong&gt;root nameserver&lt;/strong&gt; (one of 13 root server IPs) → gets the TLD nameserver address&lt;/li&gt;
&lt;li&gt;Queries the &lt;strong&gt;TLD nameserver&lt;/strong&gt; (e.g., &lt;code&gt;.com&lt;/code&gt;, &lt;code&gt;.io&lt;/code&gt;) → gets the authoritative nameserver address&lt;/li&gt;
&lt;li&gt;Queries the &lt;strong&gt;authoritative nameserver&lt;/strong&gt; for the domain → gets the final record&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Answer is returned to the client and cached at each layer according to the TTL
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client → Stub Resolver → Recursive Resolver → Root NS → TLD NS → Auth NS
                                      ← ← ← (answer cached + returned) ← ← ←
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;/p&gt;
On a cache miss the recursive resolver walks root, TLD, and authoritative nameservers iteratively; each answer is cached by its TTL so subsequent queries resolve instantly.





&lt;h3&gt;
  
  
  DNS Record Types
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Record&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;A&lt;/td&gt;
&lt;td&gt;IPv4 address for a hostname&lt;/td&gt;
&lt;td&gt;&lt;code&gt;api.example.com → 93.184.216.34&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AAAA&lt;/td&gt;
&lt;td&gt;IPv6 address for a hostname&lt;/td&gt;
&lt;td&gt;&lt;code&gt;api.example.com → 2606:2800::1&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CNAME&lt;/td&gt;
&lt;td&gt;Alias; points to another hostname&lt;/td&gt;
&lt;td&gt;&lt;code&gt;www.example.com → example.com&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MX&lt;/td&gt;
&lt;td&gt;Mail exchange server(s), with priority&lt;/td&gt;
&lt;td&gt;&lt;code&gt;example.com → 10 mail.example.com&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TXT&lt;/td&gt;
&lt;td&gt;Arbitrary text; used for SPF, DKIM, domain verification&lt;/td&gt;
&lt;td&gt;&lt;code&gt;v=spf1 include:_spf.google.com ~all&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NS&lt;/td&gt;
&lt;td&gt;Authoritative nameservers for a zone&lt;/td&gt;
&lt;td&gt;&lt;code&gt;example.com → ns1.route53.aws&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SOA&lt;/td&gt;
&lt;td&gt;Start of Authority; zone metadata&lt;/td&gt;
&lt;td&gt;Serial, refresh, retry, expire, TTL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PTR&lt;/td&gt;
&lt;td&gt;Reverse DNS (IP → name)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;34.216.184.93.in-addr.arpa → api.example.com&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SRV&lt;/td&gt;
&lt;td&gt;Service location (protocol, port, host)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;_http._tcp.example.com → 0 5 80 api.example.com&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CAA&lt;/td&gt;
&lt;td&gt;Certificate Authority Authorization&lt;/td&gt;
&lt;td&gt;Limits which CAs may issue certs for the domain&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  TTL and Caching
&lt;/h3&gt;

&lt;p&gt;TTL (Time To Live) is set on each record and controls how long resolvers cache the answer. Low TTLs (30–60 seconds) enable fast failover; high TTLs (3600+) reduce resolver load and query latency. The classic incident pattern: TTL is 3600, a record is changed, but old IPs continue serving traffic for up to an hour because downstream caches haven't expired yet.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Before a planned IP change or failover, lower the TTL to 60–120 seconds at least 2× the current TTL in advance (so caches expire before the change window). After the change, restore the original TTL.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  DNS Debugging
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Basic A record lookup&lt;/span&gt;
dig example.com

&lt;span class="c"&gt;# Query a specific record type&lt;/span&gt;
dig example.com MX
dig example.com TXT

&lt;span class="c"&gt;# Query a specific nameserver directly (bypass local cache)&lt;/span&gt;
dig @8.8.8.8 example.com A

&lt;span class="c"&gt;# Trace the full resolution chain from root&lt;/span&gt;
dig +trace example.com

&lt;span class="c"&gt;# Reverse lookup (PTR)&lt;/span&gt;
dig &lt;span class="nt"&gt;-x&lt;/span&gt; 93.184.216.34

&lt;span class="c"&gt;# Show only the answer section, short output&lt;/span&gt;
dig +short example.com

&lt;span class="c"&gt;# Check DNS propagation from the authoritative server&lt;/span&gt;
dig @ns1.example.com example.com A +norecurse

&lt;span class="c"&gt;# nslookup (simpler, available everywhere)&lt;/span&gt;
nslookup example.com
nslookup &lt;span class="nt"&gt;-type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;MX example.com 8.8.8.8
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use the &lt;a href="https://opscanopy.com/reverse-dns-ptr/" rel="noopener noreferrer"&gt;Reverse DNS / PTR&lt;/a&gt; tool to look up the hostname registered for any IP — useful for verifying that mail server PTR records are set correctly and for identifying infrastructure from IPs you see in logs.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;code&gt;/etc/hosts&lt;/code&gt; takes precedence over DNS on most Linux systems. During incident response, check whether a stale hosts entry is masking a real DNS record.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  HTTP and HTTPS/TLS
&lt;/h2&gt;

&lt;h3&gt;
  
  
  HTTP Methods and Semantics
&lt;/h3&gt;

&lt;p&gt;HTTP is the application-layer protocol powering almost every API and web interface in a DevOps toolchain.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Semantics&lt;/th&gt;
&lt;th&gt;Idempotent&lt;/th&gt;
&lt;th&gt;Safe&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;GET&lt;/td&gt;
&lt;td&gt;Retrieve a resource&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HEAD&lt;/td&gt;
&lt;td&gt;Retrieve headers only&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;POST&lt;/td&gt;
&lt;td&gt;Submit data / create resource&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PUT&lt;/td&gt;
&lt;td&gt;Replace a resource entirely&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PATCH&lt;/td&gt;
&lt;td&gt;Partial update&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DELETE&lt;/td&gt;
&lt;td&gt;Remove a resource&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OPTIONS&lt;/td&gt;
&lt;td&gt;Describe allowed methods&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  HTTP Status Code Families
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Range&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;th&gt;Common Examples&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1xx&lt;/td&gt;
&lt;td&gt;Informational&lt;/td&gt;
&lt;td&gt;101 Switching Protocols (WebSocket upgrade)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2xx&lt;/td&gt;
&lt;td&gt;Success&lt;/td&gt;
&lt;td&gt;200 OK, 201 Created, 204 No Content&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3xx&lt;/td&gt;
&lt;td&gt;Redirection&lt;/td&gt;
&lt;td&gt;301 Moved Permanently, 302 Found, 304 Not Modified&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4xx&lt;/td&gt;
&lt;td&gt;Client error&lt;/td&gt;
&lt;td&gt;400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found, 429 Too Many Requests&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5xx&lt;/td&gt;
&lt;td&gt;Server error&lt;/td&gt;
&lt;td&gt;500 Internal Server Error, 502 Bad Gateway, 503 Service Unavailable, 504 Gateway Timeout&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; In a reverse proxy setup, &lt;code&gt;502 Bad Gateway&lt;/code&gt; usually means the upstream (your app) returned an invalid response or crashed. &lt;code&gt;504 Gateway Timeout&lt;/code&gt; means the upstream was too slow. These two codes point you toward the application tier, not the proxy.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  The TLS Handshake
&lt;/h3&gt;

&lt;p&gt;HTTPS wraps HTTP inside TLS (Transport Layer Security). The handshake negotiates a shared symmetric key using asymmetric cryptography, then all subsequent data is encrypted with that key.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TLS 1.3 handshake (simplified):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client → Server: ClientHello  (supported cipher suites, TLS version, random nonce, key_share)
Server → Client: ServerHello  (chosen cipher, key_share)
Server → Client: Certificate  (server's X.509 cert chain)
Server → Client: Finished     (handshake complete, encrypted)
Client → Server: Finished     (handshake complete, encrypted)
--- Application data now flows encrypted ---
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;TLS 1.3 reduces the handshake to 1 round-trip (1-RTT), and supports 0-RTT resumption for returning clients, significantly reducing latency versus TLS 1.2.&lt;/p&gt;

&lt;h3&gt;
  
  
  Certificates
&lt;/h3&gt;

&lt;p&gt;An X.509 certificate binds a public key to a domain name (or wildcard like &lt;code&gt;*.example.com&lt;/code&gt;), signed by a Certificate Authority (CA) that browsers and operating systems trust. Key fields:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Subject / CN (Common Name):&lt;/strong&gt; The hostname the cert covers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SAN (Subject Alternative Names):&lt;/strong&gt; Additional hostnames (modern certs use SAN; CN alone is deprecated)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Issuer:&lt;/strong&gt; The CA that signed the certificate&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validity period:&lt;/strong&gt; &lt;code&gt;Not Before&lt;/code&gt; / &lt;code&gt;Not After&lt;/code&gt; — expiry causes widespread outages&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Key usage / Extended Key Usage:&lt;/strong&gt; Restricts what the cert can be used for
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Inspect a remote certificate&lt;/span&gt;
openssl s_client &lt;span class="nt"&gt;-connect&lt;/span&gt; example.com:443 &lt;span class="nt"&gt;-servername&lt;/span&gt; example.com &amp;lt;/dev/null 2&amp;gt;/dev/null | openssl x509 &lt;span class="nt"&gt;-noout&lt;/span&gt; &lt;span class="nt"&gt;-text&lt;/span&gt;

&lt;span class="c"&gt;# Check expiry date only&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; | openssl s_client &lt;span class="nt"&gt;-connect&lt;/span&gt; example.com:443 2&amp;gt;/dev/null | openssl x509 &lt;span class="nt"&gt;-noout&lt;/span&gt; &lt;span class="nt"&gt;-dates&lt;/span&gt;

&lt;span class="c"&gt;# Verify a cert file locally&lt;/span&gt;
openssl x509 &lt;span class="nt"&gt;-in&lt;/span&gt; cert.pem &lt;span class="nt"&gt;-noout&lt;/span&gt; &lt;span class="nt"&gt;-text&lt;/span&gt;

&lt;span class="c"&gt;# Test TLS with curl, verbose (shows handshake)&lt;/span&gt;
curl &lt;span class="nt"&gt;-vvv&lt;/span&gt; https://example.com 2&amp;gt;&amp;amp;1 | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-A5&lt;/span&gt; &lt;span class="s2"&gt;"TLS&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;SSL&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;certificate"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; Certificate expiry is one of the most common causes of production outages. Automate renewal (Let's Encrypt / Certbot, AWS ACM auto-renew, cert-manager in Kubernetes) and set monitoring alerts at 30 days and 7 days before expiry.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Load Balancing and Reverse Proxies
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What a Load Balancer Does
&lt;/h3&gt;

&lt;p&gt;A load balancer distributes incoming connections or requests across a pool of backend servers. Goals: eliminate single points of failure, allow horizontal scaling, and enable zero-downtime deploys through rolling updates and connection draining.&lt;/p&gt;

&lt;h3&gt;
  
  
  L4 vs L7 Load Balancing
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;L4 (Transport layer):&lt;/strong&gt; Routes based on IP address and TCP/UDP port only. The load balancer forwards packets or proxies TCP connections without inspecting content. Fast and protocol-agnostic.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Examples: AWS NLB, HAProxy in TCP mode, Linux IPVS&lt;/li&gt;
&lt;li&gt;Use for: raw TCP throughput, non-HTTP protocols (databases, gRPC over raw TCP, game servers), lowest possible latency&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;L7 (Application layer):&lt;/strong&gt; Understands HTTP(S). Can inspect and route on Host header, URL path, request method, cookies, or any header. Can terminate TLS, add/remove headers, rewrite URLs, and return synthetic responses.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Examples: AWS ALB, NGINX, HAProxy in HTTP mode, Envoy, Traefik, Caddy&lt;/li&gt;
&lt;li&gt;Use for: microservices routing, canary deployments, A/B testing, authentication offloading, WebSocket upgrades&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;L4&lt;/th&gt;
&lt;th&gt;L7&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Routing basis&lt;/td&gt;
&lt;td&gt;IP + port&lt;/td&gt;
&lt;td&gt;Host, path, headers, cookies&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TLS termination&lt;/td&gt;
&lt;td&gt;Pass-through or terminate&lt;/td&gt;
&lt;td&gt;Terminate + re-encrypt (mTLS)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Health checks&lt;/td&gt;
&lt;td&gt;TCP connect&lt;/td&gt;
&lt;td&gt;HTTP GET / gRPC health&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Latency overhead&lt;/td&gt;
&lt;td&gt;Minimal&lt;/td&gt;
&lt;td&gt;Slightly higher&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Observability&lt;/td&gt;
&lt;td&gt;Connection-level&lt;/td&gt;
&lt;td&gt;Request-level metrics&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  &lt;/p&gt;
L4 load balancers forward TCP/UDP connections by IP and port alone; L7 load balancers inspect HTTP and can route each request by host, path, headers, or cookies.





&lt;h3&gt;
  
  
  Load Balancing Algorithms
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Algorithm&lt;/th&gt;
&lt;th&gt;Behaviour&lt;/th&gt;
&lt;th&gt;Best For&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Round Robin&lt;/td&gt;
&lt;td&gt;Distribute in order&lt;/td&gt;
&lt;td&gt;Homogeneous backends&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Least Connections&lt;/td&gt;
&lt;td&gt;Route to backend with fewest active connections&lt;/td&gt;
&lt;td&gt;Variable request duration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IP Hash&lt;/td&gt;
&lt;td&gt;Route same client IP to same backend&lt;/td&gt;
&lt;td&gt;Session affinity (sticky sessions)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Weighted Round Robin&lt;/td&gt;
&lt;td&gt;Distribute proportionally by weight&lt;/td&gt;
&lt;td&gt;Mixed-capacity backends&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Random&lt;/td&gt;
&lt;td&gt;Pick at random&lt;/td&gt;
&lt;td&gt;Simple, low overhead&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Health Checks
&lt;/h3&gt;

&lt;p&gt;Health checks detect failed backends and remove them from rotation. Key parameters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Path / port:&lt;/strong&gt; What the load balancer probes (e.g., &lt;code&gt;GET /healthz&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Interval:&lt;/strong&gt; How often to probe (e.g., every 10 seconds)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Timeout:&lt;/strong&gt; How long to wait for a response before counting a failure&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Healthy threshold:&lt;/strong&gt; Consecutive successes required to mark healthy&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unhealthy threshold:&lt;/strong&gt; Consecutive failures required to remove from rotation&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Design &lt;code&gt;/healthz&lt;/code&gt; to return 200 only when the application is genuinely ready to serve traffic — not just that the process started. Check database connectivity, cache availability, and any critical dependency.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Common Reverse Proxy / Load Balancer Software
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;NGINX:&lt;/strong&gt; High-performance HTTP server and reverse proxy; excellent for static file serving, TLS termination, upstream proxying&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HAProxy:&lt;/strong&gt; Purpose-built proxy; extremely reliable, detailed metrics, supports both L4 and L7&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Envoy:&lt;/strong&gt; Cloud-native L7 proxy; the data plane behind Istio, Consul Connect, and AWS App Mesh; first-class gRPC support&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Traefik:&lt;/strong&gt; Auto-discovers Docker/Kubernetes services and configures itself from labels/annotations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Caddy:&lt;/strong&gt; Automatic HTTPS with built-in Let's Encrypt; simple configuration syntax
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Test if NGINX config is valid before reload&lt;/span&gt;
nginx &lt;span class="nt"&gt;-t&lt;/span&gt;

&lt;span class="c"&gt;# Reload NGINX without dropping connections&lt;/span&gt;
nginx &lt;span class="nt"&gt;-s&lt;/span&gt; reload

&lt;span class="c"&gt;# Check upstream health in HAProxy (stats page)&lt;/span&gt;
&lt;span class="c"&gt;# Enable in haproxy.cfg: stats uri /haproxy-stats&lt;/span&gt;
curl http://localhost/haproxy-stats
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Firewalls and Security Groups
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Stateful vs Stateless Filtering
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Stateless firewalls&lt;/strong&gt; (e.g., AWS Network ACLs, classic ACL on a router) evaluate each packet independently against a rule list. They have no memory of connection state. To allow return traffic for an outbound TCP connection, you must explicitly permit inbound traffic on the ephemeral port range.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stateful firewalls&lt;/strong&gt; (e.g., Linux &lt;code&gt;iptables&lt;/code&gt;/&lt;code&gt;nftables&lt;/code&gt;, AWS Security Groups, &lt;code&gt;firewalld&lt;/code&gt;) track connection state. If outbound traffic is permitted, return packets for that connection are automatically allowed — you only write rules for traffic initiation.&lt;/p&gt;

&lt;h3&gt;
  
  
  iptables Basics
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;iptables&lt;/code&gt; organises rules into &lt;strong&gt;tables&lt;/strong&gt; (filter, nat, mangle, raw) and &lt;strong&gt;chains&lt;/strong&gt; (INPUT, FORWARD, OUTPUT, plus custom). The &lt;code&gt;filter&lt;/code&gt; table is the default and handles accept/drop decisions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# List all filter rules with line numbers&lt;/span&gt;
iptables &lt;span class="nt"&gt;-L&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nt"&gt;--line-numbers&lt;/span&gt;

&lt;span class="c"&gt;# Allow inbound SSH&lt;/span&gt;
iptables &lt;span class="nt"&gt;-A&lt;/span&gt; INPUT &lt;span class="nt"&gt;-p&lt;/span&gt; tcp &lt;span class="nt"&gt;--dport&lt;/span&gt; 22 &lt;span class="nt"&gt;-j&lt;/span&gt; ACCEPT

&lt;span class="c"&gt;# Allow established/related return traffic (stateful)&lt;/span&gt;
iptables &lt;span class="nt"&gt;-A&lt;/span&gt; INPUT &lt;span class="nt"&gt;-m&lt;/span&gt; conntrack &lt;span class="nt"&gt;--ctstate&lt;/span&gt; ESTABLISHED,RELATED &lt;span class="nt"&gt;-j&lt;/span&gt; ACCEPT

&lt;span class="c"&gt;# Drop everything else inbound (default deny)&lt;/span&gt;
iptables &lt;span class="nt"&gt;-P&lt;/span&gt; INPUT DROP

&lt;span class="c"&gt;# Allow a specific source IP to reach port 5432&lt;/span&gt;
iptables &lt;span class="nt"&gt;-A&lt;/span&gt; INPUT &lt;span class="nt"&gt;-s&lt;/span&gt; 10.0.1.0/24 &lt;span class="nt"&gt;-p&lt;/span&gt; tcp &lt;span class="nt"&gt;--dport&lt;/span&gt; 5432 &lt;span class="nt"&gt;-j&lt;/span&gt; ACCEPT

&lt;span class="c"&gt;# Save rules (Debian/Ubuntu)&lt;/span&gt;
iptables-save &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /etc/iptables/rules.v4

&lt;span class="c"&gt;# View NAT table (SNAT/DNAT/MASQUERADE rules)&lt;/span&gt;
iptables &lt;span class="nt"&gt;-t&lt;/span&gt; nat &lt;span class="nt"&gt;-L&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;code&gt;nftables&lt;/code&gt; is the modern replacement for &lt;code&gt;iptables&lt;/code&gt; on Linux. It uses a unified table/chain model with improved performance and atomic rule updates. Both remain common; many distributions still default to &lt;code&gt;iptables&lt;/code&gt; wrappers over &lt;code&gt;nftables&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Cloud Security Groups
&lt;/h3&gt;

&lt;p&gt;Cloud security groups (AWS, GCP, Azure) are &lt;strong&gt;stateful, VM-level virtual firewalls&lt;/strong&gt; applied to network interfaces. Key characteristics:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rules specify direction (inbound/outbound), protocol, port range, and source/destination (CIDR or another security group ID)&lt;/li&gt;
&lt;li&gt;Default deny: traffic is blocked unless explicitly permitted&lt;/li&gt;
&lt;li&gt;Changes take effect immediately — no restart needed&lt;/li&gt;
&lt;li&gt;In AWS, Security Groups are distinct from Network ACLs (NACLs); NACLs are stateless and operate at the subnet level
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# List security groups for a VPC (AWS CLI)&lt;/span&gt;
aws ec2 describe-security-groups &lt;span class="nt"&gt;--filters&lt;/span&gt; &lt;span class="s2"&gt;"Name=vpc-id,Values=vpc-0abc123"&lt;/span&gt;

&lt;span class="c"&gt;# Describe inbound rules for a specific group&lt;/span&gt;
aws ec2 describe-security-groups &lt;span class="nt"&gt;--group-ids&lt;/span&gt; sg-0abc123 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s1"&gt;'SecurityGroups[*].IpPermissions'&lt;/span&gt;

&lt;span class="c"&gt;# Add an inbound rule (allow HTTPS from anywhere)&lt;/span&gt;
aws ec2 authorize-security-group-ingress &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--group-id&lt;/span&gt; sg-0abc123 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--protocol&lt;/span&gt; tcp &lt;span class="nt"&gt;--port&lt;/span&gt; 443 &lt;span class="nt"&gt;--cidr&lt;/span&gt; 0.0.0.0/0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Common Firewall Patterns
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Rule&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Allow SSH from bastion only&lt;/td&gt;
&lt;td&gt;Inbound TCP 22 from bastion security group&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Allow all outbound internet&lt;/td&gt;
&lt;td&gt;Outbound all, 0.0.0.0/0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Restrict database to app tier&lt;/td&gt;
&lt;td&gt;Inbound TCP 5432 from app security group only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Allow health checks&lt;/td&gt;
&lt;td&gt;Inbound TCP 80/443 from load balancer security group&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Block ICMP ping&lt;/td&gt;
&lt;td&gt;No inbound rule for protocol ICMP (implicit deny)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; Avoid &lt;code&gt;0.0.0.0/0&lt;/code&gt; on inbound rules for management ports (SSH, RDP, database ports). Use a bastion/jump host, VPN, or AWS Session Manager instead.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  NAT
&lt;/h2&gt;

&lt;p&gt;Network Address Translation allows hosts with private IP addresses to communicate with the public internet by rewriting IP addresses in transit.&lt;/p&gt;

&lt;h3&gt;
  
  
  SNAT (Source NAT / Masquerade)
&lt;/h3&gt;

&lt;p&gt;The most common form: a router or gateway rewrites the source IP of outbound packets from a private address to its own public IP, maintaining a connection-tracking table to forward return traffic back to the correct private host.&lt;/p&gt;

&lt;p&gt;In cloud environments this is the &lt;strong&gt;NAT Gateway&lt;/strong&gt; (AWS) or &lt;strong&gt;Cloud NAT&lt;/strong&gt; (GCP). Private subnet instances route internet-bound traffic to the NAT gateway; return traffic is forwarded back automatically.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Enable IP forwarding on a Linux NAT host&lt;/span&gt;
&lt;span class="nb"&gt;echo &lt;/span&gt;1 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /proc/sys/net/ipv4/ip_forward
sysctl &lt;span class="nt"&gt;-w&lt;/span&gt; net.ipv4.ip_forward&lt;span class="o"&gt;=&lt;/span&gt;1

&lt;span class="c"&gt;# MASQUERADE rule: outbound traffic on eth0 gets the host's public IP&lt;/span&gt;
iptables &lt;span class="nt"&gt;-t&lt;/span&gt; nat &lt;span class="nt"&gt;-A&lt;/span&gt; POSTROUTING &lt;span class="nt"&gt;-o&lt;/span&gt; eth0 &lt;span class="nt"&gt;-j&lt;/span&gt; MASQUERADE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  DNAT (Destination NAT / Port Forwarding)
&lt;/h3&gt;

&lt;p&gt;DNAT rewrites the destination IP (and optionally port) of inbound packets. This is how port forwarding works: traffic arriving at a public IP on port 8080 is redirected to an internal host at port 80.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Forward external port 8080 to internal host 10.0.1.5:80&lt;/span&gt;
iptables &lt;span class="nt"&gt;-t&lt;/span&gt; nat &lt;span class="nt"&gt;-A&lt;/span&gt; PREROUTING &lt;span class="nt"&gt;-p&lt;/span&gt; tcp &lt;span class="nt"&gt;--dport&lt;/span&gt; 8080 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-j&lt;/span&gt; DNAT &lt;span class="nt"&gt;--to-destination&lt;/span&gt; 10.0.1.5:80

&lt;span class="c"&gt;# Also enable forwarding for the redirected traffic&lt;/span&gt;
iptables &lt;span class="nt"&gt;-A&lt;/span&gt; FORWARD &lt;span class="nt"&gt;-p&lt;/span&gt; tcp &lt;span class="nt"&gt;-d&lt;/span&gt; 10.0.1.5 &lt;span class="nt"&gt;--dport&lt;/span&gt; 80 &lt;span class="nt"&gt;-j&lt;/span&gt; ACCEPT
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  NAT in Kubernetes
&lt;/h3&gt;

&lt;p&gt;Kubernetes relies heavily on NAT internally:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;kube-proxy&lt;/code&gt; (iptables mode) installs DNAT rules that redirect Service ClusterIP traffic to a real Pod IP&lt;/li&gt;
&lt;li&gt;NodePort Services use DNAT to redirect external traffic arriving on a node's port to the correct pod&lt;/li&gt;
&lt;li&gt;CNI plugins implement pod-to-internet SNAT for pods with no public IP&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  MAC Addresses and ARP
&lt;/h2&gt;

&lt;h3&gt;
  
  
  MAC Addresses
&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;MAC (Media Access Control) address&lt;/strong&gt; is a 48-bit Layer 2 hardware address assigned to a network interface. It is written as six colon- or hyphen-separated pairs of hex bytes: &lt;code&gt;aa:bb:cc:dd:ee:ff&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The first three bytes (OUI — Organizationally Unique Identifier) identify the manufacturer; the last three bytes are device-specific. MAC addresses are used for delivery within a single Layer 2 segment (broadcast domain). Routers do not forward MAC addresses between segments — IP (Layer 3) handles inter-segment routing.&lt;/p&gt;

&lt;p&gt;Use the &lt;a href="https://opscanopy.com/mac-address-formatter/" rel="noopener noreferrer"&gt;MAC Address Formatter&lt;/a&gt; to normalize MAC addresses between colon, hyphen, and Cisco dotted-quad formats, and to look up the manufacturer OUI for any address.&lt;/p&gt;

&lt;h3&gt;
  
  
  ARP — Address Resolution Protocol
&lt;/h3&gt;

&lt;p&gt;ARP resolves an IPv4 address to a MAC address within a local subnet. When host A wants to send a packet to &lt;code&gt;10.0.1.5&lt;/code&gt; (same subnet), it broadcasts an ARP request: "Who has &lt;code&gt;10.0.1.5&lt;/code&gt;? Tell &lt;code&gt;10.0.1.1&lt;/code&gt;." The host with that IP replies with its MAC address. A then writes the MAC into the Ethernet frame header and transmits.&lt;/p&gt;

&lt;p&gt;ARP replies are cached in an &lt;strong&gt;ARP table&lt;/strong&gt; (also called ARP cache) with a short TTL (typically 20–30 minutes on Linux).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Show the ARP cache&lt;/span&gt;
arp &lt;span class="nt"&gt;-n&lt;/span&gt;
ip neigh show

&lt;span class="c"&gt;# Send a gratuitous ARP (announce MAC change)&lt;/span&gt;
arping &lt;span class="nt"&gt;-I&lt;/span&gt; eth0 &lt;span class="nt"&gt;-c&lt;/span&gt; 3 10.0.1.5

&lt;span class="c"&gt;# Flush the ARP cache for an interface&lt;/span&gt;
ip neigh flush dev eth0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;ARP in virtual/cloud environments:&lt;/strong&gt; Virtual switches and hypervisors handle ARP within a physical host. Cloud providers use proxy ARP or ARP suppression to avoid broadcast storms across large Layer 2 domains. In Kubernetes, the CNI plugin handles L2 within a node; cross-node traffic is routed at L3.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; ARP spoofing (sending gratuitous ARP replies with a false MAC) is a common Man-in-the-Middle attack vector on flat L2 networks. Mitigation: Dynamic ARP Inspection (DAI) on managed switches, or move to routed L3-only designs.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Network Troubleshooting Toolkit
&lt;/h2&gt;

&lt;p&gt;Systematic troubleshooting follows the OSI model from the bottom up: is the interface up? Can I reach the gateway? Can I resolve DNS? Can I complete a TCP handshake? Can I get an HTTP 200? Each step isolates a layer. The commands below (&lt;code&gt;ping&lt;/code&gt;, &lt;code&gt;dig&lt;/code&gt;, &lt;code&gt;ss&lt;/code&gt;, &lt;code&gt;tcpdump&lt;/code&gt;) are Linux utilities; see &lt;a href="https://opscanopy.com/learn/guides/linux-for-devops/" rel="noopener noreferrer"&gt;Linux for DevOps&lt;/a&gt; for the broader shell environment they live in.&lt;/p&gt;

&lt;h3&gt;
  
  
  ping — ICMP Reachability
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Basic connectivity test&lt;/span&gt;
ping &lt;span class="nt"&gt;-c&lt;/span&gt; 4 8.8.8.8

&lt;span class="c"&gt;# Set packet size (test MTU issues)&lt;/span&gt;
ping &lt;span class="nt"&gt;-c&lt;/span&gt; 4 &lt;span class="nt"&gt;-s&lt;/span&gt; 1472 8.8.8.8   &lt;span class="c"&gt;# 1472 + 28 byte ICMP/IP header = 1500 (standard MTU)&lt;/span&gt;

&lt;span class="c"&gt;# Ping with interval&lt;/span&gt;
ping &lt;span class="nt"&gt;-i&lt;/span&gt; 0.2 &lt;span class="nt"&gt;-c&lt;/span&gt; 20 10.0.1.5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What it tells you:&lt;/strong&gt; Whether L3 reachability exists and roughly what the round-trip latency looks like. ICMP blocked by a firewall will make a host appear unreachable even when TCP services are fine.&lt;/p&gt;

&lt;h3&gt;
  
  
  traceroute / mtr — Path Discovery
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Trace the route to a host (UDP probes on Linux)&lt;/span&gt;
traceroute 8.8.8.8

&lt;span class="c"&gt;# Use ICMP probes (works where UDP is blocked)&lt;/span&gt;
traceroute &lt;span class="nt"&gt;-I&lt;/span&gt; 8.8.8.8

&lt;span class="c"&gt;# Use TCP SYN on port 80 (works through most firewalls)&lt;/span&gt;
traceroute &lt;span class="nt"&gt;-T&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 80 8.8.8.8

&lt;span class="c"&gt;# mtr: real-time traceroute with packet loss per hop&lt;/span&gt;
mtr 8.8.8.8

&lt;span class="c"&gt;# mtr non-interactive report (good for pasting into tickets)&lt;/span&gt;
mtr &lt;span class="nt"&gt;--report&lt;/span&gt; &lt;span class="nt"&gt;--report-cycles&lt;/span&gt; 20 8.8.8.8
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What it tells you:&lt;/strong&gt; Which hop introduces latency, where packet loss starts, and whether traffic takes an unexpected path.&lt;/p&gt;

&lt;h3&gt;
  
  
  dig — DNS Interrogation
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Full record lookup with TTL&lt;/span&gt;
dig example.com A

&lt;span class="c"&gt;# Query a specific resolver&lt;/span&gt;
dig @1.1.1.1 example.com

&lt;span class="c"&gt;# Trace resolution from root&lt;/span&gt;
dig +trace example.com

&lt;span class="c"&gt;# Check DNSSEC&lt;/span&gt;
dig +dnssec example.com

&lt;span class="c"&gt;# Check MX records&lt;/span&gt;
dig example.com MX +short

&lt;span class="c"&gt;# Reverse lookup&lt;/span&gt;
dig &lt;span class="nt"&gt;-x&lt;/span&gt; 93.184.216.34
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What it tells you:&lt;/strong&gt; Whether DNS resolution succeeds, which nameserver answered, the TTL remaining on a cached record, and whether DNSSEC is valid.&lt;/p&gt;

&lt;h3&gt;
  
  
  ss / netstat — Socket State
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# All listening TCP sockets with process name&lt;/span&gt;
ss &lt;span class="nt"&gt;-tnlp&lt;/span&gt;

&lt;span class="c"&gt;# All established connections&lt;/span&gt;
ss &lt;span class="nt"&gt;-tn&lt;/span&gt; state established

&lt;span class="c"&gt;# Count connections per remote IP (find abusive clients)&lt;/span&gt;
ss &lt;span class="nt"&gt;-tn&lt;/span&gt; state established | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $5}'&lt;/span&gt; | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;: &lt;span class="nt"&gt;-f1&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; | &lt;span class="nb"&gt;uniq&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-rn&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt;

&lt;span class="c"&gt;# Socket summary (total connections by state)&lt;/span&gt;
ss &lt;span class="nt"&gt;-s&lt;/span&gt;

&lt;span class="c"&gt;# Legacy equivalent (older systems)&lt;/span&gt;
netstat &lt;span class="nt"&gt;-tnlp&lt;/span&gt;
netstat &lt;span class="nt"&gt;-s&lt;/span&gt;   &lt;span class="c"&gt;# protocol statistics&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What it tells you:&lt;/strong&gt; What is actually listening, whether connections are accumulating in &lt;code&gt;TIME_WAIT&lt;/code&gt; or &lt;code&gt;CLOSE_WAIT&lt;/code&gt;, and which process owns a port.&lt;/p&gt;

&lt;h3&gt;
  
  
  tcpdump — Packet Capture
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Capture all traffic on eth0&lt;/span&gt;
tcpdump &lt;span class="nt"&gt;-i&lt;/span&gt; eth0

&lt;span class="c"&gt;# Capture only traffic on port 443&lt;/span&gt;
tcpdump &lt;span class="nt"&gt;-i&lt;/span&gt; eth0 port 443

&lt;span class="c"&gt;# Capture TCP SYN packets (new connection attempts)&lt;/span&gt;
tcpdump &lt;span class="nt"&gt;-i&lt;/span&gt; eth0 &lt;span class="s1"&gt;'tcp[tcpflags] &amp;amp; (tcp-syn) != 0'&lt;/span&gt;

&lt;span class="c"&gt;# Capture ICMP&lt;/span&gt;
tcpdump &lt;span class="nt"&gt;-i&lt;/span&gt; eth0 icmp

&lt;span class="c"&gt;# Write to file for Wireshark analysis&lt;/span&gt;
tcpdump &lt;span class="nt"&gt;-i&lt;/span&gt; eth0 &lt;span class="nt"&gt;-w&lt;/span&gt; /tmp/capture.pcap

&lt;span class="c"&gt;# Read a capture file&lt;/span&gt;
tcpdump &lt;span class="nt"&gt;-r&lt;/span&gt; /tmp/capture.pcap

&lt;span class="c"&gt;# Show packet contents in ASCII&lt;/span&gt;
tcpdump &lt;span class="nt"&gt;-i&lt;/span&gt; eth0 &lt;span class="nt"&gt;-A&lt;/span&gt; port 80

&lt;span class="c"&gt;# Capture DNS traffic&lt;/span&gt;
tcpdump &lt;span class="nt"&gt;-i&lt;/span&gt; eth0 udp port 53
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What it tells you:&lt;/strong&gt; Exactly what traffic is on the wire. Indispensable for confirming that packets are being sent and received, diagnosing TLS errors, and verifying firewall behavior.&lt;/p&gt;

&lt;h3&gt;
  
  
  curl — HTTP Testing
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Basic HTTP request&lt;/span&gt;
curl https://example.com

&lt;span class="c"&gt;# Follow redirects, show headers&lt;/span&gt;
curl &lt;span class="nt"&gt;-L&lt;/span&gt; &lt;span class="nt"&gt;-I&lt;/span&gt; https://example.com

&lt;span class="c"&gt;# Verbose output (full request/response headers, TLS handshake)&lt;/span&gt;
curl &lt;span class="nt"&gt;-v&lt;/span&gt; https://example.com

&lt;span class="c"&gt;# Test with a specific DNS resolution (bypass DNS)&lt;/span&gt;
curl &lt;span class="nt"&gt;--resolve&lt;/span&gt; example.com:443:93.184.216.34 https://example.com

&lt;span class="c"&gt;# Time each phase of the request&lt;/span&gt;
curl &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;DNS: %{time_namelookup}s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;Connect: %{time_connect}s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;TLS: %{time_appconnect}s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;First byte: %{time_starttransfer}s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;Total: %{time_total}s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; /dev/null &lt;span class="nt"&gt;-s&lt;/span&gt; https://example.com

&lt;span class="c"&gt;# Send a POST with JSON body&lt;/span&gt;
curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://api.example.com/v1/items &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"name":"widget"}'&lt;/span&gt;

&lt;span class="c"&gt;# Test a health endpoint&lt;/span&gt;
curl &lt;span class="nt"&gt;-sf&lt;/span&gt; https://api.example.com/healthz &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"UNHEALTHY"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What it tells you:&lt;/strong&gt; Whether an HTTP service is reachable, what headers and status codes it returns, how long each phase of the request takes, and whether TLS is configured correctly.&lt;/p&gt;

&lt;h3&gt;
  
  
  nc (netcat) — TCP/UDP Probe
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Test TCP connectivity to a host/port&lt;/span&gt;
nc &lt;span class="nt"&gt;-zv&lt;/span&gt; 10.0.1.5 5432

&lt;span class="c"&gt;# Test UDP&lt;/span&gt;
nc &lt;span class="nt"&gt;-zuv&lt;/span&gt; 10.0.1.5 53

&lt;span class="c"&gt;# Listen for TCP connections on a port (useful to verify firewall rules)&lt;/span&gt;
nc &lt;span class="nt"&gt;-l&lt;/span&gt; 8080

&lt;span class="c"&gt;# Simple port scan of a range&lt;/span&gt;
nc &lt;span class="nt"&gt;-zv&lt;/span&gt; 10.0.1.5 20-25
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What it tells you:&lt;/strong&gt; Whether a TCP handshake can be completed to a specific port, isolating network/firewall issues from application issues.&lt;/p&gt;

&lt;h3&gt;
  
  
  Quick Troubleshooting Checklist
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Interface up?          ip link show
2. IP assigned?           ip addr show
3. Default route?         ip route show
4. Can reach gateway?     ping &amp;lt;gateway IP&amp;gt;
5. Can reach internet?    ping 8.8.8.8
6. DNS working?           dig google.com
7. Target TCP port open?  nc -zv &amp;lt;host&amp;gt; &amp;lt;port&amp;gt;
8. HTTP response?         curl -v http://&amp;lt;host&amp;gt;:&amp;lt;port&amp;gt;
9. TLS valid?             curl -v https://&amp;lt;host&amp;gt;
10. App responding?       curl https://&amp;lt;host&amp;gt;/healthz
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Common Networking Interview Questions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Q: What is the difference between a router and a switch?&lt;/strong&gt;&lt;br&gt;
A: A switch operates at Layer 2 — it forwards Ethernet frames within a single broadcast domain using MAC address tables. A router operates at Layer 3 — it forwards IP packets between different networks using routing tables, connecting separate subnets.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: How many usable hosts are in a /27 subnet?&lt;/strong&gt;&lt;br&gt;
A: Host bits = 32 − 27 = 5. Total addresses = 2⁵ = 32. Usable hosts = 32 − 2 = 30 (subtract network address and broadcast address).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: You can ping a server but cannot SSH to it. What is the likely cause?&lt;/strong&gt;&lt;br&gt;
A: ICMP is allowed (so L3 is fine) but TCP port 22 is blocked. Most likely causes: firewall rule or security group blocks inbound port 22, SSH daemon is not running, or SSH is listening on a non-standard port. Check: &lt;code&gt;nc -zv &amp;lt;host&amp;gt; 22&lt;/code&gt; and &lt;code&gt;ss -tnlp | grep :22&lt;/code&gt; on the server.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: What happens when you type a URL into a browser?&lt;/strong&gt;&lt;br&gt;
A: DNS resolves the hostname to an IP. A TCP connection is established (3-way handshake). TLS handshake occurs if HTTPS. An HTTP GET request is sent. The server processes and returns a response. The browser renders the HTML, making additional requests for assets (CSS, JS, images). Keep-alive connections reuse the TCP connection for multiple requests.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: What is the difference between NAT and a proxy?&lt;/strong&gt;&lt;br&gt;
A: NAT rewrites IP headers at L3/L4 transparently — the application is unaware. A proxy operates at L7, establishing separate connections on each side and understanding the application protocol (HTTP, etc.). A proxy can inspect, log, and modify application-level data; NAT cannot.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: What does a 502 Bad Gateway error mean?&lt;/strong&gt;&lt;br&gt;
A: The reverse proxy or load balancer received an invalid response (or no response) from the upstream server. The proxy is reachable, but the backend application is not responding correctly — crashed, overloaded, or returning a malformed response.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: How would you verify that a DNS change has propagated?&lt;/strong&gt;&lt;br&gt;
A: Query the authoritative nameserver directly (&lt;code&gt;dig @ns1.example.com example.com&lt;/code&gt;) to confirm the change is live at the source. Then query public resolvers (&lt;code&gt;dig @8.8.8.8&lt;/code&gt;, &lt;code&gt;dig @1.1.1.1&lt;/code&gt;) to check whether their caches have expired. Use &lt;code&gt;dig +trace&lt;/code&gt; to walk the resolution chain. Also check the TTL on the old record to understand the maximum propagation delay.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: Explain the TCP TIME_WAIT state and why it matters.&lt;/strong&gt;&lt;br&gt;
A: After the active close side sends the final ACK, it enters TIME_WAIT for 2×MSL (Maximum Segment Lifetime, typically 60 seconds). This prevents delayed duplicate packets from a closed connection being accepted as part of a new connection with the same 4-tuple. Under high connection rates, large numbers of TIME_WAIT sockets can exhaust ephemeral ports. Mitigation: enable &lt;code&gt;SO_REUSEADDR&lt;/code&gt;, increase ephemeral port range, or use persistent connections / connection pooling.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: What is VLAN and why is it used?&lt;/strong&gt;&lt;br&gt;
A: A VLAN (Virtual LAN) logically segments a physical L2 network into isolated broadcast domains using 802.1Q tagging on Ethernet frames. Traffic in different VLANs cannot communicate without routing through a Layer 3 device. VLANs are used to isolate environments (prod/staging/dev), separate traffic types (management vs. data), and improve security without requiring separate physical switches.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: What is BGP and where do DevOps engineers encounter it?&lt;/strong&gt;&lt;br&gt;
A: BGP (Border Gateway Protocol) is the routing protocol that exchanges routes between autonomous systems on the internet. DevOps engineers encounter it in AWS Direct Connect (BGP advertises your on-premises routes to AWS), Transit Gateway, and in tools like MetalLB (which uses BGP to advertise Kubernetes service IPs to upstream routers in bare-metal clusters).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Prefer a day-by-day path? This is covered in &lt;a href="https://opscanopy.com/mission-90/" rel="noopener noreferrer"&gt;&lt;strong&gt;Mission 90 Days 11–13&lt;/strong&gt;&lt;/a&gt; — a free 90-day guided DevOps program with browser terminal missions.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;Networking knowledge is most powerful when combined with hands-on command-line proficiency. Pair this with the command-line side in &lt;a href="https://opscanopy.com/learn/guides/linux-for-devops/" rel="noopener noreferrer"&gt;Linux for DevOps&lt;/a&gt;, where you'll see how the same &lt;code&gt;ss&lt;/code&gt;, &lt;code&gt;ip&lt;/code&gt;, and &lt;code&gt;tcpdump&lt;/code&gt; commands fit into a broader systems administration workflow.&lt;/p&gt;

&lt;p&gt;For immediate hands-on practice, use the subnet tools linked throughout this guide — in particular the &lt;a href="https://opscanopy.com/subnet-calculator/" rel="noopener noreferrer"&gt;Subnet Calculator&lt;/a&gt; and &lt;a href="https://opscanopy.com/subnet-splitter/" rel="noopener noreferrer"&gt;Subnet Splitter&lt;/a&gt; — to work through VPC design exercises until the CIDR arithmetic feels automatic.&lt;/p&gt;

</description>
      <category>networking</category>
      <category>devops</category>
      <category>tcpip</category>
      <category>dns</category>
    </item>
    <item>
      <title>AWS AI Practitioner (AIF-C01): 14-Day Study Plan, Exam Guide, and Free Mock</title>
      <dc:creator>opscanopy</dc:creator>
      <pubDate>Mon, 07 Sep 2026 15:35:44 +0000</pubDate>
      <link>https://dev.to/opscanopy/aws-ai-practitioner-aif-c01-14-day-study-plan-exam-guide-and-free-mock-3mog</link>
      <guid>https://dev.to/opscanopy/aws-ai-practitioner-aif-c01-14-day-study-plan-exam-guide-and-free-mock-3mog</guid>
      <description>&lt;p&gt;A study plan for the AWS Certified AI Practitioner exam written for someone with no AI, ML, or AWS background, at one to one and a half hours a day. One main course, one question bank, this guide, and a free full-length mock. Everything here follows &lt;strong&gt;exam guide version 1.1, published 30 April 2026&lt;/strong&gt;, which added agentic AI, Amazon Bedrock AgentCore, Kiro, Strands Agents, and Amazon Nova to the syllabus and dropped several services that older study material still teaches. If a resource you are using does not mention agents or AgentCore, it predates the current exam.&lt;/p&gt;

&lt;p&gt;When you are ready to test yourself, the &lt;a href="https://opscanopy.com/tests/aws-ai-practitioner/" rel="noopener noreferrer"&gt;free 65-question AIF-C01 mock&lt;/a&gt; on this site follows the real domain weighting and explains every answer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Exam at a glance
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Fact&lt;/th&gt;
&lt;th&gt;Detail&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Exam code&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;AIF-C01 (AWS Certified AI Practitioner)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Questions&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;65 (50 scored + 15 unscored, not identified)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Duration&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;90 minutes (about 83 seconds per question)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Passing score&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;700 on a scaled range of 100–1,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cost&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;USD 100 (50% off until 30 September 2026, see below)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Validity&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;3 years&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Delivery&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Pearson VUE test centre or OnVUE online proctoring&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Question types&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Multiple choice, multiple response, ordering, matching&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Hands-on or coding&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Current exam guide&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Version 1.1, 30 April 2026&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Domain weights
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Domain&lt;/th&gt;
&lt;th&gt;Weight&lt;/th&gt;
&lt;th&gt;≈ Scored questions&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1 · Fundamentals of AI and ML&lt;/td&gt;
&lt;td&gt;20%&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2 · Fundamentals of generative AI&lt;/td&gt;
&lt;td&gt;24%&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3 · Applications of foundation models&lt;/td&gt;
&lt;td&gt;28%&lt;/td&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4 · Guidelines for responsible AI&lt;/td&gt;
&lt;td&gt;14%&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5 · Security, compliance, and governance for AI solutions&lt;/td&gt;
&lt;td&gt;14%&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key:&lt;/strong&gt; 52% of the exam is generative AI and foundation models. Amazon Bedrock is the centre of the exam: its features (Knowledge Bases, Agents, Guardrails, model choice, Prompt Management) and the &lt;em&gt;prompt engineering vs RAG vs fine-tuning vs distillation&lt;/em&gt; decision are the most frequently tested topics. Since the 2026 update, agentic AI (AgentCore, Strands Agents, the Model Context Protocol) sits alongside them. Spend your time there.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Questions are scenario-based: "A company needs to do X with the least operational overhead. Which service?" Memorising definitions is not enough; you need to know &lt;em&gt;when&lt;/em&gt; to use each service and each approach.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is the AWS AI Practitioner worth it, and who is it for
&lt;/h2&gt;

&lt;p&gt;AWS wrote AIF-C01 for people who &lt;strong&gt;use&lt;/strong&gt; AI on AWS rather than build it: analysts, product managers, project leads, sales engineers, and developers who need to talk credibly about AI and pick the right service. The target candidate has up to six months of exposure to AI and ML on AWS, and the exam guide puts model development, feature engineering, hyperparameter tuning, and pipeline building explicitly out of scope.&lt;/p&gt;

&lt;p&gt;It is worth taking if you fit that description. The exam costs USD 100, half that under the current promotion, needs roughly 30 to 40 hours of preparation, and the syllabus is a well-organised tour of the concepts and services that appear in real generative AI projects. Recruiters and managers recognise the AWS badge, and in 2026 an AI credential differentiates a CV more than the far more common Cloud Practitioner.&lt;/p&gt;

&lt;p&gt;It is not worth taking if you are already a working ML engineer or data scientist. You will find it too basic, and the AWS Certified Machine Learning Engineer – Associate (MLA-C01) is the credential that reflects your skills. The AI Practitioner rarely changes a salary band on its own; it amplifies whatever role you already have by adding demonstrable AI literacy.&lt;/p&gt;

&lt;h2&gt;
  
  
  How hard is the AIF-C01, and how long does it take to prepare
&lt;/h2&gt;

&lt;p&gt;It is a foundational exam and the easiest of the AWS AI certifications. There is no code, no maths, and no console work. The difficulty is vocabulary and discrimination: the exam uses a large set of terms (tokens, embeddings, RAG, RLHF, distillation, MCP) and a large set of similar-sounding services, and asks you to pick the right one for a scenario under time pressure.&lt;/p&gt;

&lt;p&gt;Plan on &lt;strong&gt;14 days at one to one and a half hours a day&lt;/strong&gt; if you are new to both AI and AWS, which is the schedule below. If you already work with AWS, one week is enough, and if you already work with generative AI tools, three to five days of service-mapping and practice tests will do.&lt;/p&gt;

&lt;p&gt;Two facts about scoring shape how you should prepare. First, 83 seconds per question means you cannot deliberate; the qualifier words in the stem ("least operational overhead", "most cost-effective", "no ML expertise", "full control") decide the answer, and you should learn to spot them. Second, the 700 pass mark is a scaled score, and AWS does not publish a raw-percentage equivalent. Treat consistent scores above 80% on realistic practice tests as the signal that you are ready.&lt;/p&gt;

&lt;h2&gt;
  
  
  Exam cost, discounts, and free vouchers
&lt;/h2&gt;

&lt;p&gt;The list price is &lt;strong&gt;USD 100&lt;/strong&gt;, charged through Pearson VUE in your local currency at Pearson's exchange rate, plus any local tax. In India, for example, the fee is billed in INR with GST added.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Until 30 September 2026 the exam is 50% off, and passing it earns a free Cloud Practitioner exam.&lt;/strong&gt; The promotion code is &lt;code&gt;AIF2CLOUD&lt;/code&gt;. The terms: register for AIF-C01 between 26 May and 30 September 2026, pass by 30 September 2026 at 23:59 local time, and you receive a voucher for the AWS Certified Cloud Practitioner (CLF-C02) exam to be used by 30 November 2026. One per person, only for exams in pass status, and it cannot be combined with other offers.&lt;/p&gt;

&lt;p&gt;Two standing benefits also apply. After you pass any AWS certification exam you receive a 50% discount voucher toward your next one, so AIF-C01 is a cheap way to halve the cost of an associate exam later. And AWS Skill Builder offers the official exam-prep plan and a 20-question official practice set free of charge.&lt;/p&gt;

&lt;p&gt;Note for non-English candidates: the Italian and German versions of the exam are retired after 15 October 2026. English, French, Spanish, Portuguese, Japanese, Korean, Chinese, and Arabic remain available.&lt;/p&gt;

&lt;h2&gt;
  
  
  AI Practitioner vs Cloud Practitioner: which first
&lt;/h2&gt;

&lt;p&gt;Neither exam is a prerequisite for the other. They share a format (65 questions, 90 minutes, 700 to pass, USD 100) and a level, and they differ in subject.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;AWS Certified Cloud Practitioner (CLF-C02)&lt;/th&gt;
&lt;th&gt;AWS Certified AI Practitioner (AIF-C01)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;What it covers&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Cloud concepts, core services (EC2, S3, VPC, IAM), billing, security basics&lt;/td&gt;
&lt;td&gt;AI/ML and GenAI concepts, Bedrock, SageMaker AI, agentic AI, responsible AI, AI security&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Best for&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Career switchers into cloud; anyone new to AWS&lt;/td&gt;
&lt;td&gt;People in AI-adjacent roles; anyone who already knows AWS basics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Recognition&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The most widely held AWS credential&lt;/td&gt;
&lt;td&gt;Newer and rarer, so it differentiates more&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Assumed knowledge&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;Familiarity with core services, IAM, shared responsibility, pricing&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Take &lt;strong&gt;Cloud Practitioner first&lt;/strong&gt; if you are switching careers into cloud and have never used AWS. You cannot reason about Bedrock security or vector stores without knowing what a VPC, an IAM role, or an S3 bucket is, and CLF-C02 is what entry-level cloud job postings ask for.&lt;/p&gt;

&lt;p&gt;Take &lt;strong&gt;AI Practitioner first&lt;/strong&gt; if you already have some AWS exposure, or if your job is about AI rather than infrastructure. Until 30 September 2026 this is also the cheaper order: pass AIF-C01 at half price and Cloud Practitioner is free.&lt;/p&gt;

&lt;h3&gt;
  
  
  AWS AI Practitioner vs Microsoft Azure AI Fundamentals (AI-900)
&lt;/h3&gt;

&lt;p&gt;AI-900 is Microsoft's equivalent foundational AI exam: about USD 99, no prerequisites, and a similar mix of AI and ML concepts with Azure services in place of AWS ones. The concepts transfer almost entirely; the service names do not. Choose by the cloud your organisation runs. If you are undecided, AIF-C01 currently has the richer generative AI and agentic AI coverage, and AWS's promotion makes it cheaper.&lt;/p&gt;

&lt;h3&gt;
  
  
  AWS AI Practitioner vs Google Cloud Generative AI Leader
&lt;/h3&gt;

&lt;p&gt;Google's Generative AI Leader certification, launched in 2025, is aimed at business leaders rather than practitioners: it is about USD 99, focuses on generative AI strategy and Google Cloud's Gemini and Vertex AI offerings, and contains little classical ML. AIF-C01 is broader, covering traditional ML fundamentals, responsible AI, and security in more depth. Pick Google's exam if your organisation is a Google Cloud shop and your role is strategic; pick AIF-C01 for a more technical foundation.&lt;/p&gt;

&lt;h3&gt;
  
  
  AWS AI Practitioner vs AWS Machine Learning Engineer – Associate (MLA-C01)
&lt;/h3&gt;

&lt;p&gt;MLA-C01 is the next step up, not an alternative. It is an associate-level, hands-on exam (about USD 150, 65 questions, 130 minutes) covering data preparation, model training and tuning, deployment, and MLOps on SageMaker AI. It assumes you build and operate ML systems. Take AIF-C01 first if you are new to AI; go straight to MLA-C01 if you already do ML engineering. Passing MLA-C01 also renews an AI Practitioner certification.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 14-day plan
&lt;/h2&gt;

&lt;p&gt;Week one is concepts, week two is practice. The plan assumes Stephane Maarek's Udemy course (about 10 hours); any current course works, so map its sections to the days below. Check that the course you pick covers AgentCore, Strands Agents, and the Model Context Protocol; if it does not, supplement those topics from this guide.&lt;/p&gt;

&lt;h3&gt;
  
  
  Week 1: concepts
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Day&lt;/th&gt;
&lt;th&gt;Focus&lt;/th&gt;
&lt;th&gt;What to do&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;1&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Setup and AI/ML basics&lt;/td&gt;
&lt;td&gt;Skim the official exam guide (15 minutes). Course: intro, AI vs ML vs deep learning vs generative AI vs agentic AI, supervised / unsupervised / reinforcement learning. Read Domain 1 below.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;2&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;ML lifecycle and AWS AI services&lt;/td&gt;
&lt;td&gt;Data → train → evaluate → deploy → monitor. SageMaker AI overview. Rekognition, Comprehend, Textract, Transcribe, Polly, Translate, Lex, Personalize. Note which services are &lt;em&gt;not&lt;/em&gt; on the 2026 in-scope list.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;3&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Generative AI fundamentals&lt;/td&gt;
&lt;td&gt;Tokens, embeddings, transformers, diffusion models, foundation models, context window, inference parameters, token-based pricing. Domain 2 concepts.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;4&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Prompt engineering and Bedrock&lt;/td&gt;
&lt;td&gt;Zero-shot, few-shot, chain-of-thought, negative prompts, prompt injection, context engineering. Open the Bedrock console (free tier) or spend 20 minutes in PartyRock changing prompts and temperature.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;5&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;RAG, Knowledge Bases, agents&lt;/td&gt;
&lt;td&gt;End-to-end RAG flow, chunking, vector stores. Bedrock Knowledge Bases, Guardrails, Agents. Agentic AI: MCP, memory, tool use, multi-agent patterns, AgentCore, Strands Agents. The customisation decision table.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;6&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Customising and evaluating FMs&lt;/td&gt;
&lt;td&gt;Fine-tuning, continued pre-training, distillation, RLHF. Evaluation: ROUGE, BLEU, BERTScore, LLM-as-a-judge, Bedrock Model Evaluation. Prompt caching, Provisioned Throughput vs on-demand.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;7&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Responsible AI and security&lt;/td&gt;
&lt;td&gt;Bias, fairness, explainability, SageMaker Clarify, Model Cards, human-in-the-loop. IAM, KMS, PrivateLink, CloudTrail, Macie, Artifact, Config, Trusted Advisor, the Generative AI Security Scoping Matrix, AgentCore Identity and Policy.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Week 2: practice and revision
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Day&lt;/th&gt;
&lt;th&gt;Focus&lt;/th&gt;
&lt;th&gt;What to do&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;8&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Practice test 1&lt;/strong&gt; (baseline)&lt;/td&gt;
&lt;td&gt;Take the &lt;a href="https://opscanopy.com/tests/aws-ai-practitioner/" rel="noopener noreferrer"&gt;free 65-question mock&lt;/a&gt; on this site. Set a 90-minute timer yourself; the runner does not enforce one. Ignore the score. Read the explanation for every wrong &lt;em&gt;and&lt;/em&gt; every guessed answer. Write a weak-topics list.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;9&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Revise&lt;/td&gt;
&lt;td&gt;Re-watch the course sections for your weak topics at 1.5× speed. Read the services cheat sheet below twice.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Practice test 2&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A Tutorials Dojo timed set, or the 20 official questions on AWS Skill Builder. Target 75%+. Which domain keeps failing?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;11&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Revise&lt;/td&gt;
&lt;td&gt;Bedrock deep dive, agentic AI, and responsible AI again. Read the glossary.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;12&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Practice test 3&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Another Tutorials Dojo set, or retake the mock here and compare against day 8. Target 80%+. Below 80%, move the exam three or four days out; there is no rush.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;13&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Final light revision&lt;/td&gt;
&lt;td&gt;Notes, cheat sheet, glossary only. No new content. Confirm exam time, ID, and the Pearson system check if testing online. Sleep early.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;14&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Exam day&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Arrive or log in 30 minutes early. Flag and move on. Watch for the qualifier words.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If one and a half hours a day is too heavy, stretch week one to ten days but keep the week two structure intact. Never skip the practice tests; they raise your score more than anything else.&lt;/p&gt;

&lt;h2&gt;
  
  
  Domain 1: Fundamentals of AI and ML (20%)
&lt;/h2&gt;

&lt;p&gt;Pure basics: concept-heavy, service-light. Clear definitions here are easy marks.&lt;/p&gt;

&lt;h3&gt;
  
  
  Concepts
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;AI vs ML vs deep learning vs generative AI vs &lt;strong&gt;agentic AI&lt;/strong&gt;: how each relates to the others&lt;/li&gt;
&lt;li&gt;Supervised (labelled data) vs unsupervised (clustering, anomaly detection) vs reinforcement learning (reward signal)&lt;/li&gt;
&lt;li&gt;Classification vs regression vs clustering; when ML is &lt;em&gt;not&lt;/em&gt; appropriate (deterministic rules suffice, or an exact outcome is required)&lt;/li&gt;
&lt;li&gt;Inference types: batch, real-time, &lt;strong&gt;asynchronous&lt;/strong&gt;, &lt;strong&gt;serverless&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Training vs inference; overfitting vs underfitting; bias and variance&lt;/li&gt;
&lt;li&gt;Data types: structured and unstructured, labelled and unlabelled, tabular, time-series, image, text&lt;/li&gt;
&lt;li&gt;The AI/ML pipeline: business goal → data collection → EDA → feature engineering → training → evaluation → deployment → monitoring; MLOps ideas such as repeatability, model monitoring, and retraining&lt;/li&gt;
&lt;li&gt;Sources of foundation models (open-source pre-trained vs custom-trained) and ways to use a model in production (managed API vs self-hosted endpoint)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Traditional ML vs foundation model&lt;/strong&gt;: when regulation, explainability, or operational constraints favour a conventional model on tabular data&lt;/li&gt;
&lt;li&gt;Model metrics: accuracy, precision, recall, F1 (classification), MAE and RMSE (regression); business metrics: cost per user, development cost, customer feedback, ROI&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  AWS services
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Amazon SageMaker AI&lt;/strong&gt;: build, train, and deploy custom models. Canvas (no-code), Ground Truth (labelling), Data Wrangler, Feature Store, Model Monitor (drift), Clarify (bias and explainability), JumpStart (model hub)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rekognition&lt;/strong&gt; (image and video), &lt;strong&gt;Textract&lt;/strong&gt; (text from documents), &lt;strong&gt;Comprehend&lt;/strong&gt; (NLP: sentiment, entities, PII)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transcribe&lt;/strong&gt; (speech → text), &lt;strong&gt;Polly&lt;/strong&gt; (text → speech), &lt;strong&gt;Translate&lt;/strong&gt;, &lt;strong&gt;Lex&lt;/strong&gt; (chatbots)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Personalize&lt;/strong&gt; (recommendations)&lt;/li&gt;
&lt;li&gt;For the pipeline stages, the guide now also names &lt;strong&gt;Amazon Bedrock&lt;/strong&gt;, &lt;strong&gt;Amazon Quick&lt;/strong&gt;, and &lt;strong&gt;Kiro&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Amazon Kendra, Amazon Forecast, Amazon Fraud Detector, and AWS Audit Manager appear in older study material but are not on the 2026 in-scope services list, and Fraud Detector is closed to new customers. Do not pick them as answers; the exam will not make them correct.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Exam pattern — "The company needs to train a custom model" → SageMaker AI. "Needs a ready-made API with no ML expertise" → a managed AI service (Rekognition, Comprehend, and so on). "Needs a general-purpose text or image capability" → a foundation model in Bedrock. This distinction appears in five or six questions.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Domain 2: Fundamentals of generative AI (24%)
&lt;/h2&gt;

&lt;p&gt;This is where the real weight begins. Know a one-line meaning and one example for every term.&lt;/p&gt;

&lt;h3&gt;
  
  
  Concepts
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;What a foundation model (FM) is; an LLM is one type of FM; pre-training vs fine-tuning&lt;/li&gt;
&lt;li&gt;Tokens, tokenisation, context window, embeddings, vectors, chunking, semantic similarity&lt;/li&gt;
&lt;li&gt;Transformer architecture (self-attention) at a high level; diffusion models for image generation; multimodal models&lt;/li&gt;
&lt;li&gt;Inference parameters: temperature, top-p, top-k, max tokens, stop sequences; which increase creativity and which increase determinism&lt;/li&gt;
&lt;li&gt;Prompt engineering: zero-shot, one- and few-shot, chain-of-thought, prompt templates, negative prompts, system prompts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Context engineering&lt;/strong&gt;: deciding what goes into the context window (instructions, retrieved content, memory, tool results) and keeping it relevant&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Token-based pricing&lt;/strong&gt; and its effect on cost and latency; prompt caching&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Agentic AI concepts&lt;/strong&gt;: tool use, memory (short-term vs long-term), multi-agent and orchestrator patterns, workflow orchestration, and the &lt;strong&gt;Model Context Protocol (MCP)&lt;/strong&gt; for connecting agents to external tools and data&lt;/li&gt;
&lt;li&gt;Hallucination, nondeterminism, toxicity, prompt injection and jailbreaks, and their mitigations&lt;/li&gt;
&lt;li&gt;GenAI use cases (summarisation, assistants, code generation, image and video generation, translation, search, recommendations) and disadvantages (hallucination, interpretability, inaccuracy, nondeterminism)&lt;/li&gt;
&lt;li&gt;Model selection factors: model type, performance, capabilities, constraints, compliance, cost, latency, complexity&lt;/li&gt;
&lt;li&gt;Business value metrics: ROI, efficiency, conversion rate, average revenue per user, customer lifetime value&lt;/li&gt;
&lt;li&gt;The FM lifecycle: data selection → model selection → pre-training → fine-tuning → evaluation → deployment → feedback&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  AWS services
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Amazon Bedrock&lt;/strong&gt;: serverless API access to FMs from Amazon (Nova), Anthropic, Meta, Mistral, Cohere, Stability AI, and others; pay per token&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Amazon Nova&lt;/strong&gt;: Amazon's own family of models for text, image (Nova Canvas), and video generation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Amazon Bedrock AgentCore&lt;/strong&gt;: the managed platform to run agents in production&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Strands Agents&lt;/strong&gt;: AWS's open-source SDK for building agents in code&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kiro&lt;/strong&gt;: AWS's agentic IDE for planning and writing code&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Amazon Quick&lt;/strong&gt; (formerly QuickSight): business intelligence with natural-language questions and agentic workflows&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Amazon Q Business&lt;/strong&gt; and &lt;strong&gt;Amazon Q Developer&lt;/strong&gt;: enterprise assistant over company data; developer assistant for code and AWS&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SageMaker JumpStart&lt;/strong&gt;: deploy open-source FMs onto your own endpoint (more control, more effort than Bedrock)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS Transform&lt;/strong&gt;: agentic migration and modernisation of legacy workloads&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PartyRock&lt;/strong&gt;: a free Bedrock playground that needs no AWS account (Apple, Google, or Amazon login). Useful for learning; no longer named in the exam guide&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Temperature ↑&lt;/th&gt;
&lt;th&gt;Temperature ↓ (near 0)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;More random and creative. Marketing copy, brainstorming.&lt;/td&gt;
&lt;td&gt;Deterministic and factual. Extraction, classification, code.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Agentic AI on AWS
&lt;/h3&gt;

&lt;p&gt;The April 2026 guide made agents a first-class topic. Know these five things and how they fit together.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Piece&lt;/th&gt;
&lt;th&gt;What it is&lt;/th&gt;
&lt;th&gt;When the question means it&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AI agent&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A foundation model that plans steps, calls tools or APIs, observes results, and adapts to reach a goal&lt;/td&gt;
&lt;td&gt;"Book, order, look up, then act" scenarios; multi-step tasks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Model Context Protocol (MCP)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;An open standard for exposing tools and data sources to agents through one reusable interface&lt;/td&gt;
&lt;td&gt;"Connect the agent to many internal systems without custom code per tool"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Strands Agents&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Open-source, model-driven SDK: define model, tools, and prompt in a few lines of code&lt;/td&gt;
&lt;td&gt;"Build an agent in code"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Amazon Bedrock AgentCore&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Managed platform to deploy and operate agents: Runtime, Memory, Identity, Gateway, Policy, Observability, plus Code Interpreter and Browser tools&lt;/td&gt;
&lt;td&gt;"Run agents in production securely without managing servers"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Amazon Bedrock Agents&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The managed agent feature inside Bedrock: action groups (APIs, Lambda), knowledge bases, memory&lt;/td&gt;
&lt;td&gt;"Managed agent with the least setup"&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Memory in agents is short-term (the current session's context) or long-term (facts and preferences persisted across sessions). Multi-agent systems typically use an orchestrator or supervisor that decomposes a goal and delegates to specialised agents. AgentCore Identity handles the agent's own identity and delegated access on a user's behalf; Policy in AgentCore authorises each tool call through AgentCore Gateway.&lt;/p&gt;

&lt;h2&gt;
  
  
  Domain 3: Applications of foundation models (28%)
&lt;/h2&gt;

&lt;p&gt;The largest domain. Nearly every question revolves around Bedrock. The table below answers roughly five exam questions on its own.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;Use it when&lt;/th&gt;
&lt;th&gt;Cost and effort&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Prompt engineering&lt;/strong&gt; (in-context learning)&lt;/td&gt;
&lt;td&gt;You need better output from the model as it is. Adds no new knowledge. &lt;em&gt;Try this first.&lt;/em&gt;
&lt;/td&gt;
&lt;td&gt;Cheapest, fastest&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;RAG&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The model needs fresh or private data (documents, databases), with citations. No retraining. → Bedrock Knowledge Bases&lt;/td&gt;
&lt;td&gt;Low to medium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Fine-tuning&lt;/strong&gt; (supervised or reinforcement)&lt;/td&gt;
&lt;td&gt;You need a specific tone, format, or task behaviour, and have labelled examples or a reward signal&lt;/td&gt;
&lt;td&gt;Higher; the custom model then runs on Provisioned Throughput or an on-demand custom-model deployment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Distillation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;You want a smaller, cheaper, faster model that matches a large model's accuracy on your task&lt;/td&gt;
&lt;td&gt;Medium; a teacher model generates training data for a student&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Continued pre-training&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;You want the model to absorb a whole domain (legal, medical) from large amounts of unlabelled text&lt;/td&gt;
&lt;td&gt;Most expensive&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Concepts
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Model selection criteria: cost, modality, latency, multilingual support, model size and complexity, customisation, input and output length, &lt;strong&gt;prompt caching&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Effect of inference parameters on responses&lt;/li&gt;
&lt;li&gt;RAG pipeline: documents → chunking → embeddings → vector store → retrieve → augment prompt → generate&lt;/li&gt;
&lt;li&gt;Vector stores on AWS: Amazon OpenSearch Service (Serverless or managed), Amazon Aurora PostgreSQL with pgvector, Amazon Neptune Analytics (GraphRAG), Amazon RDS for PostgreSQL, Amazon S3 Vectors; Bedrock Knowledge Bases can quick-create OpenSearch Serverless, Aurora PostgreSQL Serverless, Neptune Analytics, or S3 Vectors&lt;/li&gt;
&lt;li&gt;The role of agents in multi-step tasks and their business applications&lt;/li&gt;
&lt;li&gt;Prompt engineering constructs (context, instruction, negative prompts) and techniques (chain-of-thought, zero-shot, single-shot, few-shot, templates); best practices (specificity, experimentation, guardrails); risks (exposure, poisoning, hijacking, jailbreaking)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prompt versioning and management&lt;/strong&gt; with Bedrock Prompt Management&lt;/li&gt;
&lt;li&gt;Training elements: pre-training, fine-tuning, continued pre-training, distillation; fine-tuning methods: instruction tuning, domain adaptation, transfer learning; data preparation: curation, governance, size, labelling, representativeness, &lt;strong&gt;RLHF&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Evaluation: human-in-the-loop, benchmark datasets, Bedrock Model Evaluation; metrics ROUGE (summaries), BLEU (translation), BERTScore (semantic similarity), &lt;strong&gt;LLM-as-a-judge&lt;/strong&gt;; evaluating RAG, agents, and workflows as applications; business alignment metrics: task completion rate, user satisfaction, cost per interaction&lt;/li&gt;
&lt;li&gt;Pricing: on-demand per token vs Provisioned Throughput (dedicated capacity for steady high volume) vs custom-model deployment&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Bedrock features to memorise
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Knowledge Bases&lt;/strong&gt;: managed RAG; data from S3 and other connectors, automatic chunking, embedding, and vector storage&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Agents&lt;/strong&gt;: task decomposition, action groups (APIs, Lambda), memory&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Guardrails&lt;/strong&gt;: denied topics, content filters, sensitive-information (PII) filters, word filters, contextual grounding check; applied to input and output, across models&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Model Evaluation&lt;/strong&gt;: automatic metrics, human evaluation, or LLM-as-a-judge&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prompt Management&lt;/strong&gt;: store, version, test, and reuse prompts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom models&lt;/strong&gt;: supervised fine-tuning, reinforcement fine-tuning, distillation, continued pre-training&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Provisioned Throughput&lt;/strong&gt;: dedicated model units for predictable high-volume workloads; custom models can alternatively use on-demand custom-model deployment&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Amazon Nova and Titan&lt;/strong&gt;: Amazon's own models; Titan Image Generator and Nova Canvas embed an invisible watermark in every generated image, and a detection API confirms it&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Domain 4: Guidelines for responsible AI (14%)
&lt;/h2&gt;

&lt;p&gt;Mostly vocabulary: AWS's responsible-AI dimensions plus three or four services.&lt;/p&gt;

&lt;h3&gt;
  
  
  Concepts
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Features of responsible AI: bias, fairness, inclusivity, robustness, safety, veracity; AWS's eight dimensions add explainability, privacy and security, controllability, governance, and transparency&lt;/li&gt;
&lt;li&gt;Bias types: sampling and selection, measurement, confirmation, algorithmic; class imbalance; effects of bias and variance on demographic groups, overfitting, and underfitting&lt;/li&gt;
&lt;li&gt;Dataset characteristics: inclusive, diverse, balanced, curated sources&lt;/li&gt;
&lt;li&gt;Tools to detect and monitor bias: label-quality analysis, human audits, subgroup analysis&lt;/li&gt;
&lt;li&gt;Interpretability vs explainability, and the trade-off with performance (a linear model vs a deep neural network); when to prefer the transparent model&lt;/li&gt;
&lt;li&gt;Responsible model selection, including environmental and sustainability considerations&lt;/li&gt;
&lt;li&gt;Human-in-the-loop and human oversight for high-stakes decisions; human-centred design: user feedback mechanisms, decision transparency&lt;/li&gt;
&lt;li&gt;Legal risks: intellectual property infringement, biased outputs, hallucinations, end-user risk, loss of customer trust&lt;/li&gt;
&lt;li&gt;Transparency artefacts: model cards, AI service cards, documented limitations, open-source models, data and licensing&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  AWS services
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SageMaker Clarify&lt;/strong&gt;: bias detection before and after training, plus explainability (SHAP)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SageMaker Model Cards&lt;/strong&gt;: model documentation; &lt;strong&gt;AWS AI Service Cards&lt;/strong&gt; do the same for AWS's own services&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bedrock Guardrails&lt;/strong&gt;: toxicity, denied topics, PII; &lt;strong&gt;Bedrock Model Evaluations&lt;/strong&gt; as a transparency tool&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SageMaker Model Monitor&lt;/strong&gt;: drift and bias drift in production&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Amazon Augmented AI (A2I)&lt;/strong&gt;: human review of low-confidence predictions; the canonical human-in-the-loop example, though it is not on the current in-scope list&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Remember:&lt;/strong&gt; Trigger words — "detect bias" → Clarify. "Route low-confidence results to a human" → human review, A2I. "Document the model" → Model Cards. "Block harmful content" → Guardrails. "Explain a decision feature by feature" → Clarify (SHAP).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Domain 5: Security, compliance, and governance for AI solutions (14%)
&lt;/h2&gt;

&lt;p&gt;General AWS security applied to AI workloads. If you are new to AWS, give this an extra 20 minutes: IAM, KMS, and VPC will be unfamiliar.&lt;/p&gt;

&lt;h3&gt;
  
  
  Concepts
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Shared responsibility model: AWS secures the cloud, you secure what you put in it (IAM, data, guardrails, application)&lt;/li&gt;
&lt;li&gt;IAM: users, roles, policies, least privilege; service roles (for example Bedrock reading from S3)&lt;/li&gt;
&lt;li&gt;Encryption at rest (KMS, customer-managed keys) and in transit (TLS)&lt;/li&gt;
&lt;li&gt;Private networking: VPC, interface endpoints with AWS PrivateLink to keep Bedrock traffic off the public internet&lt;/li&gt;
&lt;li&gt;Bedrock data facts: your content is &lt;em&gt;not&lt;/em&gt; used to train base models, is &lt;em&gt;not&lt;/em&gt; shared with model providers, and stays in your Region&lt;/li&gt;
&lt;li&gt;Source citation and data origins: data lineage and cataloguing (Glue Data Catalog, Lake Formation), Model Cards&lt;/li&gt;
&lt;li&gt;Secure data engineering: data quality, privacy-enhancing technologies, access control, integrity&lt;/li&gt;
&lt;li&gt;Security and privacy considerations: threat detection, vulnerability management, prompt injection, &lt;strong&gt;data leakage prevention&lt;/strong&gt;, &lt;strong&gt;output filtering and validation&lt;/strong&gt;, &lt;strong&gt;audit trail and logging for AI interactions&lt;/strong&gt;, toxicity&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hallucination detection and grounding&lt;/strong&gt;: RAG grounding, output validation, confidence scoring; Guardrails' contextual grounding check&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AgentCore Identity&lt;/strong&gt; and &lt;strong&gt;Policy in AgentCore&lt;/strong&gt; for agent authentication and per-call authorisation&lt;/li&gt;
&lt;li&gt;Data governance: lifecycles, logging, residency, retention, monitoring&lt;/li&gt;
&lt;li&gt;Governance processes: policies, review cadence, the &lt;strong&gt;Generative AI Security Scoping Matrix&lt;/strong&gt; (five scopes from consuming a public app to training your own model), transparency standards, team training&lt;/li&gt;
&lt;li&gt;Compliance frameworks: ISO, SOC, GDPR, HIPAA; algorithmic accountability laws&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  AWS services
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;IAM&lt;/strong&gt;, &lt;strong&gt;KMS&lt;/strong&gt;, &lt;strong&gt;Secrets Manager&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CloudTrail&lt;/strong&gt; (who called which API, when), &lt;strong&gt;Bedrock model invocation logging&lt;/strong&gt; (prompt and response content to S3 or CloudWatch Logs), &lt;strong&gt;CloudWatch&lt;/strong&gt; (metrics and logs)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS Config&lt;/strong&gt; (resource compliance rules), &lt;strong&gt;AWS Trusted Advisor&lt;/strong&gt; (best-practice checks), &lt;strong&gt;AWS Well-Architected Tool&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Macie&lt;/strong&gt; (find PII in S3), &lt;strong&gt;Inspector&lt;/strong&gt; (software vulnerabilities), &lt;strong&gt;AWS Artifact&lt;/strong&gt; (download compliance reports)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Glue Data Catalog&lt;/strong&gt;, &lt;strong&gt;Lake Formation&lt;/strong&gt; (data governance and permissions)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;VPC endpoints / PrivateLink&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS Budgets&lt;/strong&gt;, &lt;strong&gt;Cost Explorer&lt;/strong&gt; (cost governance)&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; Amazon GuardDuty and AWS IAM Identity Center are explicitly &lt;em&gt;out of scope&lt;/em&gt; in the 2026 guide, and AWS Audit Manager is not on the in-scope list. Older material lists all three under this domain.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Services cheat sheet
&lt;/h2&gt;

&lt;p&gt;Around 40% of the exam is "which service?". Read the trigger phrase, recall the service. Read this table twice on day 9 and twice on day 13.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;When the question says…&lt;/th&gt;
&lt;th&gt;Service&lt;/th&gt;
&lt;th&gt;In one line&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Access foundation models via API, no infrastructure&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Amazon Bedrock&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Serverless FM access, pay per token&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Chatbot over company documents (RAG) with least effort&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Bedrock Knowledge Bases&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Managed RAG from S3 and other sources&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi-step tasks, call APIs, book or order things&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Bedrock Agents&lt;/strong&gt; / &lt;strong&gt;AgentCore&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Orchestration plus action groups or tools&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Run agents in production: runtime, memory, identity, tool gateway&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Amazon Bedrock AgentCore&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Managed agent platform&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Build an agent in code with an open-source SDK&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Strands Agents&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Model-driven agent framework&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Connect an agent to many tools through one standard&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Model Context Protocol (MCP)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Open tool-integration standard&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Block harmful content, deny topics, redact PII, check grounding&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Bedrock Guardrails&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Safety filters, model-agnostic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Version, test, and reuse prompts across teams&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Bedrock Prompt Management&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Prompt governance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compare or score models, including LLM-as-a-judge&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Bedrock Model Evaluation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Automatic, human, or judge-model evaluation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Smaller, cheaper model with a big model's accuracy&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Bedrock distillation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Teacher trains student&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Amazon's own text, image, and video models&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Amazon Nova&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Nova family in Bedrock&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Enterprise assistant over employees' company data&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Amazon Q Business&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Permission-aware assistant&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Agentic IDE that plans and writes code&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Kiro&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;AWS's AI development environment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ask natural-language questions of dashboards&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Amazon Quick&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;BI, formerly QuickSight&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Modernise mainframe or .NET workloads with agents&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;AWS Transform&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Agentic migration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Build, train, deploy a custom ML model&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Amazon SageMaker AI&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Full ML platform&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No-code ML for business analysts&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;SageMaker Canvas&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Point-and-click models&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Label training data with humans&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;SageMaker Ground Truth&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Labelling workforce&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deploy an open-source FM with full control&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;SageMaker JumpStart&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Pre-trained model hub&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Detect bias, explain predictions&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;SageMaker Clarify&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Bias metrics plus SHAP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Detect model or data drift in production&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;SageMaker Model Monitor&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Continuous monitoring&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Document a model for auditors&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;SageMaker Model Cards&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Model documentation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Human review of low-confidence predictions&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Amazon A2I&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Human-in-the-loop workflows&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Faces, objects, unsafe images in photos or video&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Rekognition&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Computer vision API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Extract text, tables, forms from scanned documents&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Textract&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;OCR and more&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sentiment, entities, key phrases, PII in text&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Comprehend&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;NLP API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Speech → text (call transcripts, subtitles)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Transcribe&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Speech recognition&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Text → lifelike speech&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Polly&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Text-to-speech&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Translate between languages&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Translate&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Neural machine translation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Voice or text chatbot with intents and slots&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Lex&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The technology behind Alexa&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Product recommendations like Amazon.com&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Personalize&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Real-time recommendations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Store embeddings for vector search&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;OpenSearch Service&lt;/strong&gt;, &lt;strong&gt;Aurora PostgreSQL&lt;/strong&gt;, &lt;strong&gt;Neptune Analytics&lt;/strong&gt;, &lt;strong&gt;S3 Vectors&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Vector stores for RAG&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Find sensitive data (PII) in S3&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Macie&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Data security&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Who called which API, and when&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;CloudTrail&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Audit log&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Keep prompts and responses for review&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Bedrock model invocation logging&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;To S3 or CloudWatch Logs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Download SOC or ISO compliance reports&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;AWS Artifact&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Compliance documents&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Check resource configuration against rules&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;AWS Config&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Configuration compliance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best-practice recommendations for the account&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Trusted Advisor&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Checks and recommendations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Find software vulnerabilities in workloads&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Amazon Inspector&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Vulnerability management&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Keep Bedrock traffic off the public internet&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;VPC endpoint (PrivateLink)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Private connectivity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Encrypt data, manage keys&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;KMS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Key management&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Decide which security controls a GenAI project needs&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Generative AI Security Scoping Matrix&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Five scopes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Glossary: the terms that appear again and again
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Term&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Token&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A small piece of text (roughly four characters, three quarters of a word). Pricing and context limits are based on tokens.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Context window&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;How many tokens the model can see in one request, input plus output.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Embedding&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A numeric vector for text or images; similar meaning → nearby vectors. The backbone of RAG.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Vector database&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Stores embeddings and performs similarity search.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Chunking&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Splitting large documents into smaller pieces before embedding.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;RAG&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Retrieval Augmented Generation: retrieve relevant data first, then insert it into the prompt before generating.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Context engineering&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Choosing and structuring what enters the context window so the model sees what matters and nothing that misleads it.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Hallucination&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The model states something false with confidence. Fixes: RAG grounding, guardrails, lower temperature, output validation, human review.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Temperature&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The randomness knob. Near 0 is deterministic; higher is creative.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Top-p / top-k&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Limit next-token candidates by probability mass (p) or count (k).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Zero-shot / few-shot&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Prompting with no examples / with a few examples.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Chain-of-thought&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Asking the model to reason step by step; improves accuracy on multi-step problems.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Prompt injection&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;User input that tries to override the model's instructions. Fixes: guardrails, input validation, least-privilege tools.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Prompt caching&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Reusing the processed form of a repeated prompt prefix to cut cost and latency.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Fine-tuning&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Adjusting model weights with labelled data (or a reward signal) for a specific task or style.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Distillation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Training a smaller student model to match a larger teacher model on a task.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;RLHF&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Reinforcement learning from human feedback: aligning a model to human preferences.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Transfer learning&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Reusing a pre-trained model for a new, related task.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Agentic AI&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Systems in which a model plans, uses tools, keeps memory, and acts toward a goal, often as several cooperating agents.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;MCP&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Model Context Protocol: an open standard that connects agents to tools and data sources.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Overfitting&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Perfect on training data, poor on new data. Fixes: more data, regularisation, early stopping.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Precision vs recall&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Precision: of everything flagged, how much was correct. Recall: of everything correct, how much was caught. Fraud and medical screening → recall matters more.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ROUGE / BLEU / BERTScore&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Quality metrics for summarisation / translation / semantic similarity against a reference.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;LLM-as-a-judge&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Using a capable model to grade another model's outputs against a rubric, validated by human spot checks.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Model drift&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Real-world data changes over time and accuracy drops. Model Monitor detects it.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Explainability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Why the model made a decision; required in regulated industries. SHAP via Clarify.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Study material
&lt;/h2&gt;

&lt;p&gt;Few resources, fully used. One course, one question bank, and the free official material. Everything else is optional.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Resource&lt;/th&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Stephane Maarek: Ultimate AWS Certified AI Practitioner&lt;/strong&gt; (Udemy), the main course&lt;/td&gt;
&lt;td&gt;About USD 10–15 on sale&lt;/td&gt;
&lt;td&gt;About 10 hours, exam-focused, beginner-friendly, quiz after each section. Udemy is almost always on sale; never pay full price. Confirm the version you buy covers the 2026 additions. &lt;a href="https://www.udemy.com/course/aws-ai-practitioner-certified/" rel="noopener noreferrer"&gt;udemy.com/course/aws-ai-practitioner-certified&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;OpsCanopy free AIF-C01 mock&lt;/strong&gt;, your day 8 baseline&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;td&gt;65 original questions in the real domain weighting, every answer explained, written against exam guide v1.1. Use it timed on day 8 and again on day 12. &lt;a href="https://opscanopy.com/tests/aws-ai-practitioner/" rel="noopener noreferrer"&gt;Take the mock&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Tutorials Dojo (Jon Bonso): AIF-C01 Practice Exams&lt;/strong&gt;, the question bank&lt;/td&gt;
&lt;td&gt;About USD 15&lt;/td&gt;
&lt;td&gt;Closest to real exam difficulty, with detailed explanations for every option. Those explanations &lt;em&gt;are&lt;/em&gt; your revision. Timed mode and review mode. &lt;a href="https://portal.tutorialsdojo.com/product/aws-certified-ai-practitioner-aif-c01-practice-exams/" rel="noopener noreferrer"&gt;portal.tutorialsdojo.com&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Official exam guide&lt;/strong&gt; (v1.1)&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;td&gt;Skim on day 1, re-read on day 13. It contains every task statement and the in-scope services list; this &lt;em&gt;is&lt;/em&gt; the syllabus. &lt;a href="https://docs.aws.amazon.com/aws-certification/latest/ai-practitioner-01/ai-practitioner-01.html" rel="noopener noreferrer"&gt;docs.aws.amazon.com exam guide&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AWS Skill Builder: exam-prep plan and official practice question set&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Free (account required)&lt;/td&gt;
&lt;td&gt;AWS's own prep course and 20 official practice questions. The best way to learn the question &lt;em&gt;style&lt;/em&gt;. Do it on day 10 or 11. &lt;a href="https://skillbuilder.aws/" rel="noopener noreferrer"&gt;skillbuilder.aws&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Andrew Brown / freeCodeCamp course&lt;/strong&gt; (YouTube), the free alternative to Maarek&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;td&gt;About 15 hours covering the full syllabus. Watch at 1.5× and skip hands-on demos if short on time. Search "AWS Certified AI Practitioner freeCodeCamp".&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;PartyRock&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Free, no AWS account&lt;/td&gt;
&lt;td&gt;A playground built on Bedrock. 20 minutes on day 4: build a tiny app, change temperature, change the prompt. Concepts click. &lt;a href="https://partyrock.aws/" rel="noopener noreferrer"&gt;partyrock.aws&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tutorials Dojo cheat sheets and AWS service FAQs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;td&gt;Service-by-service one-pagers for revision. Bedrock, SageMaker AI, and the Domain 5 security services are enough. &lt;a href="https://tutorialsdojo.com/aws-cheat-sheets/" rel="noopener noreferrer"&gt;tutorialsdojo.com/aws-cheat-sheets&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Total budget:&lt;/strong&gt; roughly USD 25–30 for course and practice tests, plus the exam fee of USD 100, or USD 50 with the &lt;code&gt;AIF2CLOUD&lt;/code&gt; promotion before 30 September 2026.&lt;/p&gt;

&lt;h2&gt;
  
  
  Exam day and question strategy
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Keywords decide the answer.&lt;/strong&gt; "Least operational overhead", "most cost-effective", "minimal ML expertise" → a managed service (Bedrock, Amazon Q, Rekognition). "Full control", "custom model" → SageMaker AI. "Needs current or private documents" → RAG. "Needs a consistent tone or format" → fine-tuning. "Must act, not just answer" → an agent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Eliminate first.&lt;/strong&gt; Two of the four options are almost always clearly wrong (wrong service, wrong domain). Remove them, then look for the trigger word in the remaining two.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Time management.&lt;/strong&gt; 83 seconds per question. If you do not understand a question in 30 seconds, flag it and move on. Keep the last 15 minutes for flagged questions. Never leave a blank: unanswered questions score as incorrect and there is no penalty for guessing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Multiple response.&lt;/strong&gt; The question states how many to select. Choose exactly that many; there is no partial credit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ordering and matching.&lt;/strong&gt; The FM lifecycle, the ML pipeline, and the RAG pipeline appear as ordering items. Memorise the sequences. Matching items pair services with use cases; the cheat sheet above is the preparation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If testing online.&lt;/strong&gt; Run the Pearson system test the day before. One screen only, clean desk, phone out of reach, no notes, government ID ready. AWS exams do not offer breaks; leaving the webcam view ends the exam.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Understand the score.&lt;/strong&gt; 700 out of 1,000 is a scaled score and AWS does not publish a raw-percentage cutoff. Scoring 80% or more consistently in practice is safe. Results appear in your AWS Certification Account within five business days.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Confidence.&lt;/strong&gt; This is a foundational exam. If you understand the concepts and service selection, you will pass. If in doubt, push the exam back three days, but do not over-prepare.&lt;/p&gt;

&lt;h2&gt;
  
  
  Take the free 65-question mock
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://opscanopy.com/tests/aws-ai-practitioner/" rel="noopener noreferrer"&gt;OpsCanopy AIF-C01 Full Mock Exam&lt;/a&gt; has 65 original questions in the real domain weighting (13 / 16 / 18 / 9 / 9), with about a dozen on the 2026 additions: agentic AI, MCP, AgentCore, Kiro, Strands Agents, distillation, prompt caching, context engineering, LLM-as-a-judge, and the Generative AI Security Scoping Matrix. Every answer explains why each wrong option fails. Take it timed on day 8, read every explanation, and retake it on day 12 to measure the difference. If you prefer to read rather than click, the &lt;a href="https://opscanopy.com/tests/aws-ai-practitioner/aif-c01-full-mock-exam/review/" rel="noopener noreferrer"&gt;answers-explained page&lt;/a&gt; lays out all 65 questions as prose.&lt;/p&gt;

&lt;p&gt;The runner uses multiple-choice and multiple-response items only; the real exam adds ordering and matching, whose content is covered here in multiple-choice form.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Exam facts (65 questions, 90 minutes, USD 100, 700/1,000, domain weights, question types) are per AWS exam guide v1.1 dated 30 April 2026 and the AWS certification page as of September 2026. Promotion terms are per Pearson VUE's AIF2CLOUD page. Prices for third-party courses are approximate and change frequently.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>certification</category>
      <category>aifc01</category>
      <category>aipractitioner</category>
    </item>
    <item>
      <title>x509: certificate signed by unknown authority — why it works in your browser but not in Go, curl or Docker</title>
      <dc:creator>opscanopy</dc:creator>
      <pubDate>Wed, 02 Sep 2026 17:20:20 +0000</pubDate>
      <link>https://dev.to/opscanopy/x509-certificate-signed-by-unknown-authority-why-it-works-in-your-browser-but-not-in-go-curl-or-26f6</link>
      <guid>https://dev.to/opscanopy/x509-certificate-signed-by-unknown-authority-why-it-works-in-your-browser-but-not-in-go-curl-or-26f6</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fopscanopy.com%2Fblog%2Fx509-certificate-signed-by-unknown-authority-hero.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fopscanopy.com%2Fblog%2Fx509-certificate-signed-by-unknown-authority-hero.svg" alt="A TLS chain with the intermediate certificate missing: leaf and root present, the link between them broken, so verification cannot reach a trusted anchor." width="1200" height="630"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You deploy a service, it calls an HTTPS endpoint, and the request dies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;x509: certificate signed by unknown authority
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then you open the same URL in Chrome and it loads with a padlock. Nothing is wrong with the certificate as far as your browser is concerned, so the natural conclusion is that your code, your container or your language is broken.&lt;/p&gt;

&lt;p&gt;It usually isn't. In the large majority of cases the server really is misconfigured, and your browser is hiding it. This post explains what the error actually means, why browsers disagree with Go and curl, and the four causes that between them account for almost every occurrence.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which one is you — 20 seconds
&lt;/h2&gt;

&lt;p&gt;The right cause depends almost entirely on &lt;em&gt;what&lt;/em&gt; you were connecting to, so start here rather than reading straight through:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What you were doing&lt;/th&gt;
&lt;th&gt;Almost always&lt;/th&gt;
&lt;th&gt;Jump to&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;docker login&lt;/code&gt; / &lt;code&gt;docker pull&lt;/code&gt; from a &lt;strong&gt;private registry&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Self-signed cert the daemon was never given&lt;/td&gt;
&lt;td&gt;Cause 4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Any HTTPS call &lt;strong&gt;from inside a container&lt;/strong&gt;, failing against &lt;em&gt;every&lt;/em&gt; host&lt;/td&gt;
&lt;td&gt;The image has no CA bundle&lt;/td&gt;
&lt;td&gt;Cause 2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A Go service, curl or CI job hitting a &lt;strong&gt;public&lt;/strong&gt; endpoint that works in Chrome&lt;/td&gt;
&lt;td&gt;The server is missing its intermediate&lt;/td&gt;
&lt;td&gt;Cause 1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Anything at all, but only &lt;strong&gt;on the office network / VPN&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;A TLS-inspecting proxy re-signed it&lt;/td&gt;
&lt;td&gt;Cause 3&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If you are here from a Docker registry error specifically, cause 4 is your answer and the fix is three commands. The rest of the post explains why the error is worded so unhelpfully, which is worth knowing the next time it appears somewhere else.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the error actually means
&lt;/h2&gt;

&lt;p&gt;TLS verification is a walk. Your client takes the certificate the server presented (the &lt;strong&gt;leaf&lt;/strong&gt;) and tries to build a path from it up to a certificate it already trusts (a &lt;strong&gt;root&lt;/strong&gt;, shipped in the system trust store). Each step is proved by a signature: the leaf is signed by an intermediate, the intermediate by the root.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;certificate signed by unknown authority&lt;/code&gt; means that walk ran out of road. The client found a certificate whose issuer it could not produce, so it never reached a trusted anchor.&lt;/p&gt;

&lt;p&gt;The wording is misleading. It rarely means the authority is genuinely unknown — DigiCert and Let's Encrypt are in every trust store on earth. It almost always means &lt;strong&gt;a link in the middle is missing&lt;/strong&gt;, so the client never got far enough to recognise the authority it does trust.&lt;/p&gt;

&lt;p&gt;That distinction is the whole debugging strategy: don't start by suspecting the root. Start by counting the links.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why your browser disagrees
&lt;/h2&gt;

&lt;p&gt;This is the part that misleads people, and it is worth understanding precisely, because it explains the entire "works in Chrome, fails in Docker" class of report.&lt;/p&gt;

&lt;p&gt;A correctly configured server sends the leaf &lt;strong&gt;and every intermediate&lt;/strong&gt; needed to chain up to a root. It does not send the root — the client already has that.&lt;/p&gt;

&lt;p&gt;When the server forgets the intermediate, clients diverge:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Browsers paper over it.&lt;/strong&gt; They cache intermediates they have seen before, and they follow the &lt;em&gt;Authority Information Access&lt;/em&gt; (AIA) extension in the leaf, which contains a URL for the issuing certificate. Chrome will quietly fetch the missing intermediate over HTTP and complete the chain. You see a padlock.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Go does not.&lt;/strong&gt; &lt;code&gt;crypto/x509&lt;/code&gt; performs no AIA fetching, by design — a verification step that makes its own outbound HTTP request is an availability and privacy problem. If the intermediate is not presented, verification fails.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;curl and OpenSSL generally do not either&lt;/strong&gt;, so they fail the same way.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because Docker, Kubernetes, Terraform, most CI runners and a large share of backend services are written in Go, "it works in the browser" is not evidence the server is fine. It is frequently evidence of exactly this bug. The browser is being generous; Go is telling you the truth.&lt;/p&gt;

&lt;h2&gt;
  
  
  Diagnose it in one command
&lt;/h2&gt;

&lt;p&gt;Before changing anything, count the certificates the server actually sends:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openssl s_client &lt;span class="nt"&gt;-connect&lt;/span&gt; example.com:443 &lt;span class="nt"&gt;-servername&lt;/span&gt; example.com &lt;span class="nt"&gt;-showcerts&lt;/span&gt; &amp;lt;/dev/null 2&amp;gt;/dev/null &lt;span class="se"&gt;\&lt;/span&gt;
  | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"BEGIN CERTIFICATE"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read the number:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;1&lt;/strong&gt; — only the leaf. The intermediate is missing. This is your bug, and it is the single most common cause.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;2 or more&lt;/strong&gt; — a chain is being sent. The problem is elsewhere; keep reading past cause 1.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;-servername&lt;/code&gt; matters. Without it OpenSSL omits SNI, and a host serving multiple sites will hand you its default certificate rather than the one you are debugging — which sends you chasing a mismatch that does not exist in production.&lt;/p&gt;

&lt;p&gt;To see what each certificate in that chain actually is — who issued it, what it covers, whether the order is right — paste the whole &lt;code&gt;-showcerts&lt;/code&gt; output into the &lt;a href="https://opscanopy.com/certificate-decoder/" rel="noopener noreferrer"&gt;Certificate Decoder &amp;amp; Chain Checker&lt;/a&gt;. It reads every X.509 field, reports the chain order with the reason it was wrong, and gives a per-link signature verdict, so you can see which link fails to connect rather than inferring it. It runs entirely in your browser, which matters when the chain belongs to an internal host.&lt;/p&gt;

&lt;h2&gt;
  
  
  The four causes, ranked
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. The server is missing its intermediate
&lt;/h3&gt;

&lt;p&gt;By a wide margin the most common. The admin installed only the leaf, or concatenated the files in the wrong order, or a renewal replaced a full-chain file with a leaf-only one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix it on the server, not in your client.&lt;/strong&gt; Nearly every issuer ships a "fullchain" file for exactly this reason:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="c1"&gt;# nginx — ssl_certificate must be leaf + intermediates, in that order&lt;/span&gt;
&lt;span class="k"&gt;ssl_certificate&lt;/span&gt;     &lt;span class="n"&gt;/etc/letsencrypt/live/example.com/fullchain.pem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;# not cert.pem&lt;/span&gt;
&lt;span class="k"&gt;ssl_certificate_key&lt;/span&gt; &lt;span class="n"&gt;/etc/letsencrypt/live/example.com/privkey.pem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The ordering rule is: &lt;strong&gt;leaf first, then each issuer in turn, root optional and usually omitted.&lt;/strong&gt; A chain assembled in reverse order fails on strict clients even though every certificate present is valid — which is precisely the failure the chain checker names for you rather than leaving you to eyeball PEM blocks.&lt;/p&gt;

&lt;p&gt;After reloading, re-run the &lt;code&gt;grep -c&lt;/code&gt; above and confirm the count went up.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Your container has no CA bundle at all
&lt;/h3&gt;

&lt;p&gt;If the failure only happens inside a container, suspect the image before the network. Minimal base images ship no trust store, so &lt;em&gt;every&lt;/em&gt; public certificate is signed by an unknown authority — the client has no roots whatsoever.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Alpine — no CA bundle by default&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apk add &lt;span class="nt"&gt;--no-cache&lt;/span&gt; ca-certificates

&lt;span class="c"&gt;# Debian/Ubuntu slim — usually present, but reinstall if it was stripped&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="nt"&gt;--no-install-recommends&lt;/span&gt; ca-certificates &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /var/lib/apt/lists/&lt;span class="k"&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For &lt;code&gt;scratch&lt;/code&gt; or distroless images, copy the bundle in from a build stage:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The tell for this cause: the same binary works on the host and fails in the container, against &lt;em&gt;any&lt;/em&gt; HTTPS host — not just one.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. A corporate proxy is re-signing traffic
&lt;/h3&gt;

&lt;p&gt;On a corporate network, an inspecting proxy terminates TLS and re-signs it with an internal CA. That CA is legitimately unknown to your container, which never received it.&lt;/p&gt;

&lt;p&gt;The tell: the leaf's issuer is your employer, not a public CA. Decode the certificate and read the issuer — if it says something like &lt;code&gt;Issuer: CN=Acme Corp Root CA&lt;/code&gt;, this is your cause.&lt;/p&gt;

&lt;p&gt;The fix is to install the corporate root into the image's trust store, not to disable verification:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; corp-root.crt /usr/local/share/ca-certificates/corp-root.crt&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;update-ca-certificates
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. The certificate really is self-signed
&lt;/h3&gt;

&lt;p&gt;Internal services, a local dev stack, a private registry. Here the error is correct: the certificate genuinely is not trusted by anyone, and nothing is misconfigured except your client's trust store.&lt;/p&gt;

&lt;p&gt;This is by far the most common way people meet this error in practice, because it is what &lt;code&gt;docker login&lt;/code&gt; and &lt;code&gt;docker pull&lt;/code&gt; against a private registry produce on day one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Docker daemon does not use the system trust store for registries.&lt;/strong&gt; It reads a per-registry directory, and the path must match the registry hostname &lt;em&gt;and port&lt;/em&gt; exactly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# note: directory name = exactly what you type after docker login,&lt;/span&gt;
&lt;span class="c"&gt;# including the port. registry.internal and registry.internal:5000&lt;/span&gt;
&lt;span class="c"&gt;# are two different directories.&lt;/span&gt;
&lt;span class="nb"&gt;sudo mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /etc/docker/certs.d/registry.internal:5000
&lt;span class="nb"&gt;sudo cp &lt;/span&gt;ca.crt /etc/docker/certs.d/registry.internal:5000/ca.crt
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl restart docker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three details that account for most of the "I did that and it still fails" follow-ups:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The file must be named &lt;code&gt;ca.crt&lt;/code&gt;.&lt;/strong&gt; Not &lt;code&gt;ca.pem&lt;/code&gt;, not &lt;code&gt;registry.crt&lt;/code&gt;. The daemon looks for that name (plus &lt;code&gt;*.cert&lt;/code&gt; / &lt;code&gt;*.key&lt;/code&gt; for client auth).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The port is part of the directory name.&lt;/strong&gt; A registry on &lt;code&gt;:5000&lt;/code&gt; needs &lt;code&gt;registry.internal:5000&lt;/code&gt;, and a registry on 443 needs the bare hostname.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Restart the daemon.&lt;/strong&gt; It reads that directory at startup, so the file appearing is not enough.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For Kubernetes pulling the same registry, this path does not apply — the kubelet uses the &lt;em&gt;node's&lt;/em&gt; system trust store, so install the CA there with &lt;code&gt;update-ca-certificates&lt;/code&gt; and restart the kubelet. This is why an image can pull fine on your laptop and fail with &lt;code&gt;ImagePullBackOff&lt;/code&gt; on the cluster.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix that is not a fix
&lt;/h2&gt;

&lt;p&gt;Every one of these has a tempting one-liner: &lt;code&gt;InsecureSkipVerify: true&lt;/code&gt; in Go, &lt;code&gt;-k&lt;/code&gt; in curl, &lt;code&gt;insecure_skip_verify&lt;/code&gt; in Terraform.&lt;/p&gt;

&lt;p&gt;These do not fix anything. They disable the check that was correctly telling you the chain is broken, and they disable it for &lt;em&gt;every&lt;/em&gt; connection that code path makes, permanently, including against an attacker. The failure you are silencing is the one case where verification did its job.&lt;/p&gt;

&lt;p&gt;If you need an escape hatch during local development, scope it to a specific CA rather than switching verification off:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;x509&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SystemCertPool&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AppendCertsFromPEM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;internalCA&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;          &lt;span class="c"&gt;// trust one extra CA&lt;/span&gt;
&lt;span class="n"&gt;cfg&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;tls&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;RootCAs&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;            &lt;span class="c"&gt;// still verifies everything&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That trusts exactly what you decided to trust, and nothing else.&lt;/p&gt;

&lt;h2&gt;
  
  
  A short checklist
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Count the certificates the server sends with &lt;code&gt;openssl s_client -showcerts&lt;/code&gt;. One means a missing intermediate — fix the server.&lt;/li&gt;
&lt;li&gt;If it fails only in a container, and against every host, install &lt;code&gt;ca-certificates&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Read the leaf's issuer. If it is your employer, install the corporate root.&lt;/li&gt;
&lt;li&gt;If it is genuinely self-signed, add that CA — do not skip verification.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The through-line: &lt;code&gt;certificate signed by unknown authority&lt;/code&gt; is nearly always a chain-assembly problem, and your browser's willingness to repair it silently is what makes it look like a client bug. Count the links first, and the cause usually names itself.&lt;/p&gt;

</description>
      <category>tls</category>
      <category>certificates</category>
      <category>docker</category>
      <category>go</category>
    </item>
    <item>
      <title>OOMKilled and exit code 137: why Kubernetes killed your pod, and what to actually change</title>
      <dc:creator>opscanopy</dc:creator>
      <pubDate>Mon, 31 Aug 2026 17:20:22 +0000</pubDate>
      <link>https://dev.to/opscanopy/oomkilled-and-exit-code-137-why-kubernetes-killed-your-pod-and-what-to-actually-change-4mdi</link>
      <guid>https://dev.to/opscanopy/oomkilled-and-exit-code-137-why-kubernetes-killed-your-pod-and-what-to-actually-change-4mdi</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fopscanopy.com%2Fblog%2Fkubernetes-oomkilled-exit-code-137-hero.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fopscanopy.com%2Fblog%2Fkubernetes-oomkilled-exit-code-137-hero.svg" alt="A pod restarting under memory pressure: the container's working set crossing its configured limit line, the kernel OOM killer terminating it, and the restart counter incrementing." width="1200" height="630"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A pod is restarting and you can't see why. The logs end mid-request with nothing that looks like a crash. Then you describe it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;    &lt;span class="na"&gt;State&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;          &lt;span class="s"&gt;Running&lt;/span&gt;
    &lt;span class="na"&gt;Last State&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;     &lt;span class="s"&gt;Terminated&lt;/span&gt;
      &lt;span class="s"&gt;Reason&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;       &lt;span class="s"&gt;OOMKilled&lt;/span&gt;
      &lt;span class="s"&gt;Exit Code&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;    &lt;span class="m"&gt;137&lt;/span&gt;
      &lt;span class="na"&gt;Started&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;      &lt;span class="s"&gt;Mon, 29 Aug 2026 09:14:02 +0000&lt;/span&gt;
      &lt;span class="na"&gt;Finished&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;     &lt;span class="s"&gt;Mon, 29 Aug 2026 09:41:37 +0000&lt;/span&gt;
    &lt;span class="na"&gt;Restart Count&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;  &lt;span class="m"&gt;6&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two things are worth knowing immediately. &lt;strong&gt;Your application did not crash&lt;/strong&gt; — it was killed from outside, by the Linux kernel, with SIGKILL. And &lt;strong&gt;there is no graceful shutdown and no stack trace&lt;/strong&gt;, because SIGKILL cannot be caught. That silence in the logs is the expected symptom, not a missing clue.&lt;/p&gt;

&lt;h2&gt;
  
  
  What 137 actually means
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;137&lt;/code&gt; is &lt;code&gt;128 + 9&lt;/code&gt;. The 128 marks "terminated by signal", and 9 is SIGKILL. So exit code 137 means &lt;em&gt;something&lt;/em&gt; sent SIGKILL — most often the kernel's OOM killer, but not always.&lt;/p&gt;

&lt;p&gt;The distinction that matters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Reason: OOMKilled&lt;/code&gt;&lt;/strong&gt; — the container exceeded its memory limit. The kernel's cgroup OOM killer terminated it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Reason: Error&lt;/code&gt;, exit code 137&lt;/strong&gt; — something else sent SIGKILL. Usually a &lt;code&gt;terminationGracePeriodSeconds&lt;/code&gt; that expired: Kubernetes sent SIGTERM, the process ignored it or shut down too slowly, and Kubernetes escalated to SIGKILL.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Read the &lt;code&gt;Reason&lt;/code&gt; field before assuming memory. The second case is a shutdown-handling bug and no amount of extra memory will fix it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl describe pod &amp;lt;pod&amp;gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-A4&lt;/span&gt; &lt;span class="s2"&gt;"Last State"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Confirm it, don't infer it
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;Restart Count&lt;/code&gt; going up with &lt;code&gt;OOMKilled&lt;/code&gt; in &lt;code&gt;Last State&lt;/code&gt; is the confirmation. To see the pattern rather than a single instance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Which containers have OOMed, across the namespace&lt;/span&gt;
kubectl get pods &lt;span class="nt"&gt;-o&lt;/span&gt; json &lt;span class="se"&gt;\&lt;/span&gt;
  | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.items[] | .metadata.name as $p
      | .status.containerStatuses[]?
      | select(.lastState.terminated.reason=="OOMKilled")
      | "\($p)\t\(.name)\trestarts=\(.restartCount)"'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also check events, which sometimes name the node-level pressure that triggered it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get events &lt;span class="nt"&gt;--field-selector&lt;/span&gt; &lt;span class="nv"&gt;reason&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;OOMKilling &lt;span class="nt"&gt;-A&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why raising the limit is often the wrong first move
&lt;/h2&gt;

&lt;p&gt;The reflex is to double &lt;code&gt;limits.memory&lt;/code&gt; and move on. Sometimes that is correct — the limit was simply set below what the workload legitimately needs. Often it isn't, and it converts a fast, obvious failure into a slow, expensive one.&lt;/p&gt;

&lt;p&gt;Ask which of these you have:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A limit that was always too low.&lt;/strong&gt; The container OOMs quickly and consistently, often within seconds of starting, at roughly the same memory figure every time. Raising the limit is the right fix.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A leak.&lt;/strong&gt; Memory climbs steadily over minutes or hours until it hits the ceiling, and the time-to-OOM is proportional to the limit. Doubling the limit doubles the time between restarts and fixes nothing. The tell is that &lt;code&gt;Started&lt;/code&gt; and &lt;code&gt;Finished&lt;/code&gt; in &lt;code&gt;Last State&lt;/code&gt; are far apart, and the interval grows when you raise the limit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A runtime that ignores the cgroup.&lt;/strong&gt; The JVM before it was container-aware, Node's default old-space, Go with no &lt;code&gt;GOMEMLIMIT&lt;/code&gt; — the runtime sizes its heap against the &lt;em&gt;node's&lt;/em&gt; total memory, not the container's limit, then confidently allocates past the ceiling. This is extremely common and looks exactly like a leak from the outside.&lt;/p&gt;

&lt;p&gt;For the JVM, use the container-aware flags rather than a fixed &lt;code&gt;-Xmx&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;JAVA_TOOL_OPTIONS&lt;/span&gt;
    &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-XX:MaxRAMPercentage=75.0"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For Go 1.19+, tell the runtime the ceiling so the garbage collector works against the real budget:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;GOMEMLIMIT&lt;/span&gt;
    &lt;span class="na"&gt;valueFrom&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;resourceFieldRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;resource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;limits.memory&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;resourceFieldRef&lt;/code&gt; reads the container's own limit, so the two can never drift apart.&lt;/p&gt;

&lt;h2&gt;
  
  
  The requests-versus-limits mistake behind most of it
&lt;/h2&gt;

&lt;p&gt;Memory and CPU behave completely differently under pressure, and conflating them causes a lot of unnecessary OOMKills.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CPU is compressible.&lt;/strong&gt; Exceed the CPU limit and you get throttled — slower, but alive.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory is not.&lt;/strong&gt; Exceed the memory limit and you are killed. There is no "a bit slower" state.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That asymmetry has a direct consequence: &lt;strong&gt;CPU limits are optional and often harmful; memory limits are essential.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The second half is the Quality of Service class, which decides who gets killed first when the &lt;em&gt;node&lt;/em&gt; runs out of memory:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;requests vs limits&lt;/th&gt;
&lt;th&gt;QoS class&lt;/th&gt;
&lt;th&gt;Eviction order&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Not set&lt;/td&gt;
&lt;td&gt;&lt;code&gt;BestEffort&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Killed first&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;requests &amp;lt; limits&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Burstable&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Middle&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;requests == limits&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Guaranteed&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Killed last&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A pod with no memory request is &lt;code&gt;BestEffort&lt;/code&gt; and is the first thing evicted under node pressure — even if it was using very little. If a workload matters, give it a request. For anything latency-sensitive, set request equal to limit and get &lt;code&gt;Guaranteed&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;requests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;512Mi"&lt;/span&gt;
    &lt;span class="na"&gt;cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;250m"&lt;/span&gt;
  &lt;span class="na"&gt;limits&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;512Mi"&lt;/span&gt;     &lt;span class="c1"&gt;# equal to request -&amp;gt; Guaranteed&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Sizing it, and checking what it costs
&lt;/h2&gt;

&lt;p&gt;Base the number on observation, not a guess. Use the working set over a representative window — including whatever your peak actually is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;max_over_time(
  container_memory_working_set_bytes{pod=~"my-app-.*", container!=""}[7d]
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Take that peak and add headroom — commonly 20–30%, more if traffic is spiky. Note that &lt;code&gt;container_memory_working_set_bytes&lt;/code&gt; is the metric the OOM killer effectively acts on, which is why it is the right one here rather than &lt;code&gt;container_memory_usage_bytes&lt;/code&gt; (which includes reclaimable cache and will overstate your need).&lt;/p&gt;

&lt;p&gt;Once you have a per-container number, the arithmetic across replicas is where mistakes creep back in — millicores and &lt;code&gt;Mi&lt;/code&gt;/&lt;code&gt;Gi&lt;/code&gt; units, multiplied by replica counts, summed across containers including sidecars. The &lt;a href="https://opscanopy.com/kubernetes-resource-calculator/" rel="noopener noreferrer"&gt;Kubernetes Resource Calculator&lt;/a&gt; totals CPU and memory requests and limits across containers and replicas and converts the units, so you can see what a deployment actually reserves on the cluster before you apply it.&lt;/p&gt;

&lt;p&gt;To be clear about what that does and does not do: it will not tell you &lt;em&gt;why&lt;/em&gt; a container OOMed — that comes from the metric above and the runtime's own behaviour. What it answers is the question that follows, which is whether the limit you just chose, multiplied by every replica, still fits the nodes you have.&lt;/p&gt;

&lt;h2&gt;
  
  
  A working order
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;kubectl describe pod&lt;/code&gt; and read &lt;code&gt;Reason&lt;/code&gt;. &lt;code&gt;OOMKilled&lt;/code&gt; is memory; &lt;code&gt;Error&lt;/code&gt; with 137 is a shutdown-grace problem instead.&lt;/li&gt;
&lt;li&gt;Check whether time-to-OOM scales with the limit. If it does, you have a leak or a runtime ignoring the cgroup — fix that, don't raise the ceiling.&lt;/li&gt;
&lt;li&gt;If the runtime is the JVM, Node or Go, make it container-aware before touching limits.&lt;/li&gt;
&lt;li&gt;Size from &lt;code&gt;max_over_time(container_memory_working_set_bytes[7d])&lt;/code&gt; plus headroom.&lt;/li&gt;
&lt;li&gt;Set a memory &lt;em&gt;request&lt;/em&gt; as well as a limit, so the pod isn't &lt;code&gt;BestEffort&lt;/code&gt; and first to be evicted.&lt;/li&gt;
&lt;li&gt;Total it across replicas and confirm it still fits the node pool.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The recurring theme: exit code 137 is a report that something exceeded a boundary, not a description of the bug. The useful question is never "how much memory should I add" but "does this workload's memory grow with time, and does its runtime know what its ceiling is".&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>memory</category>
      <category>limits</category>
      <category>debugging</category>
    </item>
    <item>
      <title>failed to solve: process \"/bin/sh -c ...\" did not complete successfully: exit code: 1</title>
      <dc:creator>opscanopy</dc:creator>
      <pubDate>Mon, 31 Aug 2026 17:19:48 +0000</pubDate>
      <link>https://dev.to/opscanopy/failed-to-solve-process-binsh-c-did-not-complete-successfully-exit-code-1-2fo8</link>
      <guid>https://dev.to/opscanopy/failed-to-solve-process-binsh-c-did-not-complete-successfully-exit-code-1-2fo8</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fopscanopy.com%2Fblog%2Fdocker-build-failed-to-solve-exit-code-1-hero.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fopscanopy.com%2Fblog%2Fdocker-build-failed-to-solve-exit-code-1-hero.svg" alt="A Dockerfile build stopping at a RUN layer: earlier layers cached and green, the failing instruction marked with its exit code, later layers never reached." width="1200" height="630"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Your build stops with a wall of text ending in this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;------
 &amp;gt; [4/8] RUN apt-get install -y curl:
------
Dockerfile:12
--------------------
  10 |     WORKDIR /app
  11 |     COPY . .
  12 | &amp;gt;&amp;gt;&amp;gt; RUN apt-get install -y curl
  13 |     
--------------------
ERROR: failed to solve: process "/bin/sh -c apt-get install -y curl" did not complete successfully: exit code: 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first thing to understand is that &lt;strong&gt;this error is not the problem&lt;/strong&gt;. It is BuildKit reporting that a command you asked it to run exited non-zero. The actual cause is in that command's output, which BuildKit has usually just truncated away.&lt;/p&gt;

&lt;p&gt;So there are two jobs here: get the real error back, then fix it. This post covers both, plus the four structural causes that produce this message far more often than anything else.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reading the error properly
&lt;/h2&gt;

&lt;p&gt;Three parts matter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;[4/8]&lt;/code&gt;&lt;/strong&gt; — which step failed, and how far the build got. Steps 1–3 succeeded.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Dockerfile:12&lt;/code&gt;&lt;/strong&gt; with the &lt;code&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/code&gt; marker — the exact line. BuildKit is precise about this; trust it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;exit code: 1&lt;/code&gt;&lt;/strong&gt; — the status your command returned. This is the &lt;em&gt;command's&lt;/em&gt; exit code, not Docker's.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last one is worth reading carefully, because some exit codes are diagnostic on their own:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Exit code&lt;/th&gt;
&lt;th&gt;Usually means&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Generic failure — the command itself will say why&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;100&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;apt-get&lt;/code&gt; could not install (unresolved package, stale index)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;126&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Found the file but could not execute it — permissions, or CRLF line endings&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;127&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Command not found — not installed, or not on &lt;code&gt;PATH&lt;/code&gt; yet&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;137&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Killed by SIGKILL — almost always the builder running out of memory&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;127&lt;/code&gt; and &lt;code&gt;137&lt;/code&gt; in particular save you a lot of time. &lt;code&gt;127&lt;/code&gt; means you are calling something that does not exist in that layer. &lt;code&gt;137&lt;/code&gt; means nothing is wrong with your command at all — the build ran out of RAM, and you should raise the memory available to the builder rather than edit the Dockerfile.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get the real output back
&lt;/h2&gt;

&lt;p&gt;BuildKit collapses successful output and truncates failed output. To see the whole thing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;--progress&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;plain &lt;span class="nt"&gt;--no-cache&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;--progress=plain&lt;/code&gt; disables the collapsing TUI and streams every line. &lt;code&gt;--no-cache&lt;/code&gt; matters more than it looks: with a warm cache, BuildKit may not re-run the earlier steps that produced the actual error message, so you get the failure without the context.&lt;/p&gt;

&lt;p&gt;If the output is long, capture it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;--progress&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;plain &lt;span class="nt"&gt;--no-cache&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt; 2&amp;gt;&amp;amp;1 | &lt;span class="nb"&gt;tee &lt;/span&gt;build.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nine times out of ten the answer is now sitting in &lt;code&gt;build.log&lt;/code&gt;, several lines above the &lt;code&gt;failed to solve&lt;/code&gt; line, in the failing command's own words.&lt;/p&gt;

&lt;h2&gt;
  
  
  Debug the layer interactively
&lt;/h2&gt;

&lt;p&gt;When the output still isn't enough, get a shell in the last layer that &lt;em&gt;did&lt;/em&gt; build. Every successful step leaves an image behind:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# grab the image id of the last good step from --progress=plain output&lt;/span&gt;
docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; &amp;lt;last-good-image-id&amp;gt; sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then run the failing command by hand. This resolves most "but it works on my machine" cases immediately, because you are now standing inside the exact filesystem the command was given — not your shell, with your &lt;code&gt;PATH&lt;/code&gt;, your installed packages and your environment variables.&lt;/p&gt;

&lt;h2&gt;
  
  
  The four structural causes
&lt;/h2&gt;

&lt;p&gt;Most instances of this error are not exotic. They are the same handful of Dockerfile mistakes.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. &lt;code&gt;apt-get install&lt;/code&gt; without a fresh index
&lt;/h3&gt;

&lt;p&gt;The single most common cause, and the most confusing, because it works one day and fails the next.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Broken — and it will pass CI for weeks before it doesn't&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; curl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These are two layers. Docker caches the &lt;code&gt;apt-get update&lt;/code&gt; layer, and keeps using it long after the package index it downloaded has gone stale upstream. Eventually &lt;code&gt;install&lt;/code&gt; asks for a package version that no longer exists at the URL the cached index recorded, and returns exit code 100.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Correct — one layer, so the index is always as fresh as the install&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="nt"&gt;--no-install-recommends&lt;/span&gt; curl &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /var/lib/apt/lists/&lt;span class="k"&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;rm -rf&lt;/code&gt; is not just hygiene: leaving the lists in the image guarantees a future reader will copy this pattern with a stale cache again.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. The command needs a shell feature it doesn't have
&lt;/h3&gt;

&lt;p&gt;Exec form does not run a shell:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"apt-get"&lt;/span&gt;, &lt;span class="s2"&gt;"install"&lt;/span&gt;, &lt;span class="s2"&gt;"-y"&lt;/span&gt;, &lt;span class="s2"&gt;"curl &amp;amp;&amp;amp; echo done"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;   &lt;span class="c"&gt;# &amp;amp;&amp;amp; is a literal argument&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is no shell here to interpret &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt;, &lt;code&gt;|&lt;/code&gt;, &lt;code&gt;&amp;gt;&lt;/code&gt;, &lt;code&gt;$VAR&lt;/code&gt; or &lt;code&gt;*&lt;/code&gt;. If you need any of those, use shell form (&lt;code&gt;RUN apt-get install -y curl &amp;amp;&amp;amp; echo done&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;The same trap bites &lt;code&gt;CMD&lt;/code&gt; and &lt;code&gt;ENTRYPOINT&lt;/code&gt; in exec form, and on Alpine there is a second layer to it: the default shell is &lt;code&gt;/bin/sh&lt;/code&gt;, not bash. A script using &lt;code&gt;[[&lt;/code&gt;, arrays or &lt;code&gt;source&lt;/code&gt; will fail with exit code 2 or 127 even though it is a perfectly good bash script.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. &lt;code&gt;COPY&lt;/code&gt; did not copy what you think
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;./scripts/build.sh      &lt;span class="c"&gt;# exit code 127: not found&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two usual reasons. First, &lt;code&gt;.dockerignore&lt;/code&gt; excluded the file — it applies to the build context before &lt;code&gt;COPY&lt;/code&gt; ever sees it, so the file silently is not there. Second, the file is there but not executable, which gives you &lt;code&gt;126&lt;/code&gt; rather than &lt;code&gt;127&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Check what actually landed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt; ./scripts    &lt;span class="c"&gt;# temporary, but it settles the question in one build&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On Windows there is a third: a script committed with CRLF line endings fails with &lt;code&gt;exec format error&lt;/code&gt; or a confusing &lt;code&gt;not found&lt;/code&gt;, because the kernel reads the shebang as &lt;code&gt;/bin/sh\r&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Architecture mismatch
&lt;/h3&gt;

&lt;p&gt;On Apple Silicon building for a linux/amd64 target, a binary downloaded at build time may be the wrong architecture, producing &lt;code&gt;exec format error&lt;/code&gt; and a non-zero exit.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;--platform&lt;/span&gt; linux/amd64 &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make the platform explicit rather than relying on the daemon's default, especially in a CI pipeline whose runners may not match your laptop.&lt;/p&gt;

&lt;h2&gt;
  
  
  Catching these before the build runs
&lt;/h2&gt;

&lt;p&gt;Causes 1–3 are all visible in the Dockerfile itself, without running anything. That is what a linter is for, and it is worth being precise about the division of labour:&lt;/p&gt;

&lt;p&gt;A linter &lt;strong&gt;cannot&lt;/strong&gt; tell you why &lt;em&gt;your specific&lt;/em&gt; &lt;code&gt;apt-get install&lt;/code&gt; returned 1 — that depends on the package index at that moment. What it can do is flag the structural patterns that make this error likely and recurrent: the split &lt;code&gt;apt-get update&lt;/code&gt; / &lt;code&gt;install&lt;/code&gt; cache trap, broken JSON exec forms, &lt;code&gt;COPY&lt;/code&gt; ordering that busts the cache on every build, and secrets baked into image history.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://opscanopy.com/dockerfile-linter/" rel="noopener noreferrer"&gt;Dockerfile Linter&lt;/a&gt; runs seventeen such rules over a real parse of the file and returns line-numbered findings with a fix for each, plus an explicit list of what it deliberately does not flag. Paste the Dockerfile, fix what it names, and the recurring instances of this error stop happening. The one-off ones you still debug with &lt;code&gt;--progress=plain&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The order to work in
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Re-run with &lt;code&gt;--progress=plain --no-cache&lt;/code&gt; and read the failing command's own output. This alone resolves most cases.&lt;/li&gt;
&lt;li&gt;Check the exit code against the table above — &lt;code&gt;127&lt;/code&gt; and &lt;code&gt;137&lt;/code&gt; in particular mean something specific.&lt;/li&gt;
&lt;li&gt;Still stuck? Shell into the last good layer and run the command by hand.&lt;/li&gt;
&lt;li&gt;Then lint the Dockerfile, so the structural version of this failure does not come back next month.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The message itself is generic by design: BuildKit does not know anything about &lt;code&gt;apt-get&lt;/code&gt;, only that a process it started returned 1. Once you stop reading &lt;code&gt;failed to solve&lt;/code&gt; as the error and start treating it as a pointer to a line number and an exit code, it becomes one of the easier Docker failures to work through.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>buildkit</category>
      <category>ci</category>
      <category>dockerfile</category>
    </item>
    <item>
      <title>Linux for DevOps Engineers</title>
      <dc:creator>opscanopy</dc:creator>
      <pubDate>Tue, 25 Aug 2026 16:03:02 +0000</pubDate>
      <link>https://dev.to/opscanopy/linux-for-devops-engineers-5616</link>
      <guid>https://dev.to/opscanopy/linux-for-devops-engineers-5616</guid>
      <description>&lt;p&gt;Linux is the operating system under every Docker container, every Kubernetes node, and the majority of cloud VMs. Before you can deploy, debug, or automate anything in a modern infrastructure stack, you need to be comfortable at the shell. This guide takes you from the absolute fundamentals — how Linux thinks about files and processes — through the tools DevOps engineers reach for daily: permissions, grep/awk/sed, systemd, SSH, package managers, network commands, and production-grade bash scripting. Follow the &lt;a href="https://opscanopy.com/learn/roadmaps/linux/" rel="noopener noreferrer"&gt;Linux roadmap&lt;/a&gt; to see how these topics connect into a full learning path.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction to Linux
&lt;/h2&gt;

&lt;p&gt;Imagine you bought a brand new car. You see the dashboard, the steering wheel, the leather seats, the touchscreen infotainment system. But what actually &lt;em&gt;moves&lt;/em&gt; the car? The engine. You rarely open the hood, you never see it during your daily drive, yet without it, nothing works. &lt;strong&gt;Linux is exactly that engine&lt;/strong&gt; for the entire modern internet. Every time you scroll Instagram, order from Uber Eats, watch Netflix, or tap your phone to pay — somewhere in a datacenter, a Linux server is doing the heavy lifting. You never see it. You never think about it. But it's powering 96% of the world's digital infrastructure in 2026.&lt;/p&gt;

&lt;p&gt;As an experienced frontend developer, you've been writing code that &lt;em&gt;runs on top of&lt;/em&gt; this engine. You've been the person designing the dashboard. Now you're about to learn what's under the hood — and that's exactly what the highest-paid DevOps engineers know cold. The transition from frontend to DevOps is one of the most valuable career moves in tech today, because you already understand how applications behave; now you'll learn how they live, breathe, scale, and recover at the infrastructure level.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Real world:&lt;/strong&gt; Think of Linux like the global air-traffic network. You don't see the radar, the control towers, or the routing systems when you fly — but they're the reason tens of thousands of flights move millions of passengers safely every single day. Linux is the air-traffic network of the internet. AWS, Azure, GCP, Netflix, Google, Facebook, WhatsApp, Spotify, Amazon — they all run on Linux.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Why Linux Dominates DevOps in 2026
&lt;/h3&gt;

&lt;p&gt;If you walked into any tech company in a major tech hub today and asked their SRE team what operating system runs production, the answer is almost universally Linux. Here's why this domination is now absolute:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;~96% of public cloud servers run Linux&lt;/strong&gt; — AWS EC2, Azure VMs, Google Compute Engine. Even Microsoft, which makes Windows, runs the majority of Azure on Linux.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;100% of containers use the Linux kernel&lt;/strong&gt; — Docker, Podman, containerd all rely on Linux namespaces and cgroups. Even "Windows containers" running on Docker Desktop spin up a hidden Linux VM underneath.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Every single Kubernetes node is Linux&lt;/strong&gt; — kubelet, kube-proxy, CNI plugins, the entire control plane is designed for Linux first.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;All major CI/CD runners are Linux&lt;/strong&gt; — GitHub Actions, GitLab Runners, Jenkins agents, ArgoCD, CircleCI default to Linux.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Every observability stack runs on Linux&lt;/strong&gt; — Prometheus, Grafana, Loki, Elastic, Datadog agents, OpenTelemetry collectors.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Edge computing and IoT&lt;/strong&gt; — Raspberry Pi, AWS Greengrass, Azure IoT Edge, all Linux.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a React developer transitioning to DevOps, this means one undeniable truth: &lt;strong&gt;you cannot avoid Linux&lt;/strong&gt;. It's not optional anymore. The good news? You already have Ubuntu 24.04 installed, which is the gold standard for DevOps learning in 2026.&lt;/p&gt;

&lt;h3&gt;
  
  
  Linux vs Windows vs macOS: The Honest Comparison
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Linux&lt;/th&gt;
&lt;th&gt;Windows&lt;/th&gt;
&lt;th&gt;macOS&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cost&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Free (most distros)&lt;/td&gt;
&lt;td&gt;~$140 per license&lt;/td&gt;
&lt;td&gt;Bundled with Apple hardware (premium)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Customization&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Unlimited — change anything&lt;/td&gt;
&lt;td&gt;Limited to UI/registry&lt;/td&gt;
&lt;td&gt;Restricted — Apple's way only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CLI Power&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Native, world-class (bash, zsh)&lt;/td&gt;
&lt;td&gt;Catching up (PowerShell, WSL2)&lt;/td&gt;
&lt;td&gt;Strong (Unix-based, zsh default)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Server Use&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~96% of internet servers&lt;/td&gt;
&lt;td&gt;~3% (legacy enterprise AD)&lt;/td&gt;
&lt;td&gt;Negligible (not a server OS)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Security&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Excellent (permissions, SELinux)&lt;/td&gt;
&lt;td&gt;Improving but still targeted&lt;/td&gt;
&lt;td&gt;Strong (sandboxed by default)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Package Mgmt&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;apt, dnf, pacman, snap, flatpak&lt;/td&gt;
&lt;td&gt;winget, choco, MSI installers&lt;/td&gt;
&lt;td&gt;brew (community), App Store&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Source Code&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;100% open source (GPL)&lt;/td&gt;
&lt;td&gt;Proprietary (closed)&lt;/td&gt;
&lt;td&gt;XNU kernel partially open, OS closed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DevOps Fit&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The de-facto standard&lt;/td&gt;
&lt;td&gt;Niche (Windows containers)&lt;/td&gt;
&lt;td&gt;Good for local dev only&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  The Linux Distro Universe
&lt;/h3&gt;

&lt;p&gt;One of the most confusing things for beginners is the sheer number of Linux distributions ("distros"). They're all Linux at the core (same kernel), but they package the system differently — different installers, different package managers, different defaults, different philosophies. Here's the map you need:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Distro&lt;/th&gt;
&lt;th&gt;Base&lt;/th&gt;
&lt;th&gt;Package Mgr&lt;/th&gt;
&lt;th&gt;Best For&lt;/th&gt;
&lt;th&gt;DevOps Use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ubuntu&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Debian&lt;/td&gt;
&lt;td&gt;apt&lt;/td&gt;
&lt;td&gt;Development, learning, desktops&lt;/td&gt;
&lt;td&gt;Dev laptops, CI runners, EC2 default&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;RHEL&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Fedora&lt;/td&gt;
&lt;td&gt;dnf/yum&lt;/td&gt;
&lt;td&gt;Enterprise production&lt;/td&gt;
&lt;td&gt;Banks, telco, govt, regulated industries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CentOS Stream&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;RHEL upstream&lt;/td&gt;
&lt;td&gt;dnf&lt;/td&gt;
&lt;td&gt;Testing RHEL features early&lt;/td&gt;
&lt;td&gt;Rolling preview of RHEL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Debian&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Independent&lt;/td&gt;
&lt;td&gt;apt&lt;/td&gt;
&lt;td&gt;Stable servers, rock-solid uptime&lt;/td&gt;
&lt;td&gt;Long-running production servers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Alpine&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Independent (musl)&lt;/td&gt;
&lt;td&gt;apk&lt;/td&gt;
&lt;td&gt;Containers — only 5MB base!&lt;/td&gt;
&lt;td&gt;99% of Docker base images&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Rocky/AlmaLinux&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;RHEL clones&lt;/td&gt;
&lt;td&gt;dnf&lt;/td&gt;
&lt;td&gt;Free RHEL alternative&lt;/td&gt;
&lt;td&gt;Post-CentOS production replacement&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Arch Linux&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Independent&lt;/td&gt;
&lt;td&gt;pacman&lt;/td&gt;
&lt;td&gt;Advanced users, customization&lt;/td&gt;
&lt;td&gt;Personal workstations (rarely prod)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Amazon Linux 2023&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Fedora-based&lt;/td&gt;
&lt;td&gt;dnf&lt;/td&gt;
&lt;td&gt;AWS-optimized workloads&lt;/td&gt;
&lt;td&gt;Default EC2/ECS/Lambda runtime&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Which distro do real DevOps engineers actually use?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Dev laptop:&lt;/strong&gt; Ubuntu 24.04 LTS or macOS&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Production servers:&lt;/strong&gt; RHEL, Amazon Linux 2023, Rocky Linux, or Ubuntu Server LTS&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Docker base images:&lt;/strong&gt; Alpine (tiny, fast), or distroless/Debian-slim&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kubernetes nodes:&lt;/strong&gt; Ubuntu, Bottlerocket (AWS), Flatcar, or Talos Linux&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Master Ubuntu first because &lt;code&gt;apt&lt;/code&gt;-based skills transfer 80% to Debian and Kali. Then learn &lt;code&gt;dnf&lt;/code&gt; for RHEL/Rocky/Amazon Linux. That covers ~95% of real-world DevOps scenarios.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Kernel vs Operating System
&lt;/h3&gt;

&lt;p&gt;This is the single most common interview blunder. Junior candidates confuse "kernel" and "OS" all the time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Linux Kernel&lt;/strong&gt; is the core program — roughly &lt;strong&gt;30+ million lines of C code&lt;/strong&gt; — written originally by Linus Torvalds in 1991 when he was a 21-year-old student in Helsinki, Finland. The kernel is just the engine — it talks to the CPU, RAM, disks, network cards, and so on. By itself, the kernel cannot give you a usable computer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;An Operating System&lt;/strong&gt; is the kernel &lt;em&gt;plus&lt;/em&gt; all the surrounding software needed to make it usable: the GNU utilities (ls, cat, grep, sed, awk), the shell (bash, zsh), system libraries (glibc), package managers (apt, dnf), and applications. So when you say "Linux," what you usually mean is "GNU/Linux."&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;uname&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt;
6.8.0-31-generic
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /etc/os-release | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-3&lt;/span&gt;
&lt;span class="nv"&gt;PRETTY_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Ubuntu 24.04 LTS"&lt;/span&gt;
&lt;span class="nv"&gt;NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Ubuntu"&lt;/span&gt;
&lt;span class="nv"&gt;VERSION_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"24.04"&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;uname&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt;
Linux dev-laptop 6.8.0-31-generic &lt;span class="c"&gt;#31-Ubuntu SMP x86_64 GNU/Linux&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Interview tip:&lt;/strong&gt; If an interviewer asks "What is Linux?" — never just say "an operating system." Say: "Linux is technically the kernel — a monolithic Unix-like kernel created by Linus Torvalds in 1991. When people say 'Linux,' they usually mean GNU/Linux, which is the kernel plus the GNU userland utilities, a shell, system libraries like glibc, and applications, all packaged by a distribution like Ubuntu or RHEL."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Open Source and the GPL
&lt;/h3&gt;

&lt;p&gt;Linux is released under the &lt;strong&gt;GNU General Public License version 2 (GPLv2)&lt;/strong&gt;. The GPL guarantees four fundamental freedoms (numbered 0 through 3, because programmers count from zero):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Freedom 0:&lt;/strong&gt; Run the program for any purpose, without restriction.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Freedom 1:&lt;/strong&gt; Study how the program works and modify it. Source code access is required.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Freedom 2:&lt;/strong&gt; Redistribute copies to help others.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Freedom 3:&lt;/strong&gt; Distribute your modified versions so the community benefits.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Why do massive corporations like Google, Meta, Amazon, and Microsoft pour billions into a "free" project? Because open-source Linux gives them zero licensing cost at scale, total customizability, security through transparency, vendor independence, and faster innovation.&lt;/p&gt;

&lt;h3&gt;
  
  
  The DevOps Salary Reality (2026)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Level&lt;/th&gt;
&lt;th&gt;Experience&lt;/th&gt;
&lt;th&gt;US (USD)&lt;/th&gt;
&lt;th&gt;Europe (EUR)&lt;/th&gt;
&lt;th&gt;India (INR)&lt;/th&gt;
&lt;th&gt;Typical Skills&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Junior DevOps&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;0-2 years&lt;/td&gt;
&lt;td&gt;$65k–100k&lt;/td&gt;
&lt;td&gt;€45k–70k&lt;/td&gt;
&lt;td&gt;₹6–12 LPA&lt;/td&gt;
&lt;td&gt;Linux basics, Git, Docker, basic CI/CD, one cloud&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Mid DevOps / SRE&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;2-5 years&lt;/td&gt;
&lt;td&gt;$100k–150k&lt;/td&gt;
&lt;td&gt;€70k–105k&lt;/td&gt;
&lt;td&gt;₹15–25 LPA&lt;/td&gt;
&lt;td&gt;Kubernetes, Terraform, Prometheus, scripting, multi-cloud&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Senior DevOps / SRE&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;5-8 years&lt;/td&gt;
&lt;td&gt;$150k–200k&lt;/td&gt;
&lt;td&gt;€105k–145k&lt;/td&gt;
&lt;td&gt;₹30–50 LPA&lt;/td&gt;
&lt;td&gt;Architecture, security, cost optimization, on-call leadership&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Lead / Staff / Principal&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;8-12+ years&lt;/td&gt;
&lt;td&gt;$190k–270k&lt;/td&gt;
&lt;td&gt;€140k–190k&lt;/td&gt;
&lt;td&gt;₹50–80+ LPA&lt;/td&gt;
&lt;td&gt;Platform engineering, multi-team coordination, strategy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DevOps Manager / Director&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;10+ years&lt;/td&gt;
&lt;td&gt;$200k–350k+&lt;/td&gt;
&lt;td&gt;€150k–230k&lt;/td&gt;
&lt;td&gt;₹70 LPA–1.5 Cr+&lt;/td&gt;
&lt;td&gt;People management, business alignment, P&amp;amp;L&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Figures are rough 2026 market signals, not guarantees. They swing widely with company size, city, cost of living, and equity — treat them as the relative gaps between levels, not absolute targets.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Linux Architecture
&lt;/h2&gt;

&lt;p&gt;Now that you know &lt;em&gt;what&lt;/em&gt; Linux is, let's open the hood and look at &lt;em&gt;how&lt;/em&gt; it actually works. Linux follows a layered architecture — each layer has a specific job, and they communicate through well-defined interfaces. If you understand this architecture clearly, you'll debug production issues 10x faster than engineers who only know commands by rote.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Kernel: The Brain of the System
&lt;/h3&gt;

&lt;p&gt;The Linux kernel is the lowest software layer above the hardware. It has six core responsibilities that you must internalize:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Process Management:&lt;/strong&gt; Creating, scheduling, pausing, killing processes. The kernel decides which process gets CPU time. Tools like &lt;code&gt;ps&lt;/code&gt;, &lt;code&gt;top&lt;/code&gt;, &lt;code&gt;htop&lt;/code&gt;, &lt;code&gt;kill&lt;/code&gt; all interact with this subsystem.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory Management:&lt;/strong&gt; Allocating RAM to processes, managing virtual memory, paging, swapping. The OOM (Out of Memory) killer lives here.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Device Drivers:&lt;/strong&gt; Translating generic OS calls into hardware-specific signals — GPU, NIC, SSD, USB, Bluetooth all speak through drivers loaded as kernel modules.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Filesystem Management:&lt;/strong&gt; Reading, writing, organizing data on disk through filesystems like ext4, xfs, btrfs, zfs, overlayfs (used by Docker).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Networking:&lt;/strong&gt; The entire TCP/IP stack, sockets, packet routing, iptables/nftables — all kernel code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security &amp;amp; Permissions:&lt;/strong&gt; User IDs, group IDs, file permissions, capabilities, SELinux/AppArmor, seccomp filters (used by Docker for sandboxing).&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Shells: Your Window to the Kernel
&lt;/h3&gt;

&lt;p&gt;You don't talk to the kernel directly — you talk to a &lt;strong&gt;shell&lt;/strong&gt;, and the shell talks to the kernel. A shell is just a program that reads commands, interprets them, and asks the kernel to do work via system calls.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Shell&lt;/th&gt;
&lt;th&gt;Default On&lt;/th&gt;
&lt;th&gt;Strengths&lt;/th&gt;
&lt;th&gt;Weaknesses&lt;/th&gt;
&lt;th&gt;DevOps Verdict&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Bash&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Ubuntu, RHEL, Debian, Alpine (ash)&lt;/td&gt;
&lt;td&gt;Universal, scripting, POSIX compliance&lt;/td&gt;
&lt;td&gt;Plain UX, no autosuggestions&lt;/td&gt;
&lt;td&gt;Must master — every server has it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Zsh&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;macOS Catalina+&lt;/td&gt;
&lt;td&gt;Themes (oh-my-zsh), autosuggestions, plugins&lt;/td&gt;
&lt;td&gt;Slightly heavier, not on servers&lt;/td&gt;
&lt;td&gt;Great for daily dev laptop use&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Fish&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;None default&lt;/td&gt;
&lt;td&gt;Friendly UX, autosuggest out of the box&lt;/td&gt;
&lt;td&gt;Non-POSIX — scripts don't transfer&lt;/td&gt;
&lt;td&gt;Personal use only, never scripts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Dash&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Ubuntu's /bin/sh&lt;/td&gt;
&lt;td&gt;Tiny, fast, POSIX-strict&lt;/td&gt;
&lt;td&gt;Minimal features&lt;/td&gt;
&lt;td&gt;Used by Ubuntu boot scripts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;PowerShell&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Windows&lt;/td&gt;
&lt;td&gt;Object pipeline, cross-platform now&lt;/td&gt;
&lt;td&gt;Heavy, alien to Unix folk&lt;/td&gt;
&lt;td&gt;Skip unless Windows-shop&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Use Zsh + oh-my-zsh on your Ubuntu dev laptop for daily comfort, but always write your shell scripts in &lt;strong&gt;Bash&lt;/strong&gt; (with &lt;code&gt;#!/usr/bin/env bash&lt;/code&gt;) so they run on every server you'll ever touch. Production servers don't have your fancy zsh setup.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  System Libraries and Utilities
&lt;/h3&gt;

&lt;p&gt;Between user programs and the kernel sit two crucial layers. &lt;strong&gt;System Libraries&lt;/strong&gt; — the most important is &lt;code&gt;glibc&lt;/code&gt; (GNU C Library). When your Node.js process needs to open a file, it calls a C function like &lt;code&gt;fopen()&lt;/code&gt; in glibc, which translates to the kernel's &lt;code&gt;open()&lt;/code&gt; system call. Alpine Linux uses &lt;code&gt;musl libc&lt;/code&gt; instead, which is why Alpine images are smaller but sometimes have subtle compatibility quirks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;System Utilities&lt;/strong&gt; — the GNU &lt;code&gt;coreutils&lt;/code&gt; package gives you &lt;code&gt;ls&lt;/code&gt;, &lt;code&gt;cp&lt;/code&gt;, &lt;code&gt;mv&lt;/code&gt;, &lt;code&gt;rm&lt;/code&gt;, &lt;code&gt;cat&lt;/code&gt;, &lt;code&gt;echo&lt;/code&gt;, &lt;code&gt;mkdir&lt;/code&gt;, &lt;code&gt;chmod&lt;/code&gt;, &lt;code&gt;chown&lt;/code&gt;, and ~100 more. Then there's &lt;code&gt;util-linux&lt;/code&gt;, &lt;code&gt;findutils&lt;/code&gt;, &lt;code&gt;grep&lt;/code&gt;, &lt;code&gt;sed&lt;/code&gt;, &lt;code&gt;awk&lt;/code&gt;, &lt;code&gt;tar&lt;/code&gt;, &lt;code&gt;gzip&lt;/code&gt; — collectively known as "the Unix toolbox."&lt;/p&gt;

&lt;h3&gt;
  
  
  User Space vs Kernel Space
&lt;/h3&gt;

&lt;p&gt;Modern CPUs have two privilege modes: &lt;strong&gt;kernel mode&lt;/strong&gt; (ring 0 on x86) where code can do &lt;em&gt;anything&lt;/em&gt;, and &lt;strong&gt;user mode&lt;/strong&gt; (ring 3) where code is sandboxed and must ask the kernel (via system calls) to do anything dangerous. Your React app, Node.js, Python scripts, even databases like PostgreSQL — all run in user space. Only the kernel and its drivers run in kernel space. You can literally see system calls with &lt;code&gt;strace&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;strace &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="nb"&gt;ls&lt;/span&gt; /tmp
% &lt;span class="nb"&gt;time     &lt;/span&gt;seconds  usecs/call     calls    errors syscall
&lt;span class="nt"&gt;------&lt;/span&gt; &lt;span class="nt"&gt;-----------&lt;/span&gt; &lt;span class="nt"&gt;-----------&lt;/span&gt; &lt;span class="nt"&gt;---------&lt;/span&gt; &lt;span class="nt"&gt;---------&lt;/span&gt; &lt;span class="nt"&gt;----------------&lt;/span&gt;
 24.31    0.000312          26        12           openat
 18.13    0.000233          19        12           &lt;span class="nb"&gt;read
 &lt;/span&gt;11.45    0.000147          12        12           close
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Linux Boot Process
&lt;/h3&gt;

&lt;p&gt;When you press the power button on a Linux machine (or click "Start instance" on an EC2 console), a tightly choreographed sequence brings the system to life. Understanding this is critical because &lt;strong&gt;most production outages happen during boot&lt;/strong&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;BIOS / UEFI:&lt;/strong&gt; The first code that runs lives in firmware on the motherboard. It initializes the CPU and looks for a bootable device. On EC2, this is the Nitro hypervisor's firmware.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;POST (Power-On Self-Test):&lt;/strong&gt; Hardware checks — RAM, keyboard, basic devices.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MBR / GPT Boot Sector:&lt;/strong&gt; Firmware reads the first sector of the boot disk to find the bootloader.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GRUB (GRand Unified Bootloader):&lt;/strong&gt; Displays the boot menu, lets you pick a kernel, and loads the kernel image and initramfs into memory. Config lives in &lt;code&gt;/boot/grub/grub.cfg&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kernel + initramfs:&lt;/strong&gt; The kernel unpacks itself, the initramfs provides essential drivers to mount the real root filesystem. Kernel mounts &lt;code&gt;/&lt;/code&gt; and starts PID 1.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;systemd (PID 1):&lt;/strong&gt; Modern init system. Reads &lt;code&gt;/etc/systemd/system/&lt;/code&gt;, starts services in dependency order, mounts filesystems from &lt;code&gt;/etc/fstab&lt;/code&gt;, brings up the network.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;multi-user.target / graphical.target:&lt;/strong&gt; systemd's "runlevel" — on servers it stops at multi-user.target (text login).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Login prompt:&lt;/strong&gt; getty on TTYs, sshd for remote, GDM for graphical.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Interview tip:&lt;/strong&gt; "Walk me through what happens when you press the power button on a Linux server until you get a login prompt." This is asked in literally every senior DevOps interview. Memorize the 8-step sequence above. Bonus points if you mention systemd targets, initramfs, and the role of &lt;code&gt;/etc/fstab&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  DevOps Tie-Ins: Why Boot Knowledge Pays
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;EC2 boot failures:&lt;/strong&gt; When an EC2 instance won't start, AWS shows you the serial console output — you see exactly which boot step failed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GRUB recovery:&lt;/strong&gt; Kernel update broke boot? You boot from a rescue ISO, chroot into the broken system, run &lt;code&gt;grub-install&lt;/code&gt; and &lt;code&gt;update-grub&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;systemd unit debugging:&lt;/strong&gt; A service won't start at boot? &lt;code&gt;systemctl status&lt;/code&gt;, &lt;code&gt;journalctl -xeu service-name&lt;/code&gt;, check dependencies with &lt;code&gt;systemctl list-dependencies&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom AMI building:&lt;/strong&gt; Packer images, custom EKS node AMIs, Bottlerocket — all require understanding what boots and when.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Container startup:&lt;/strong&gt; Containers skip BIOS/GRUB/kernel — they start at "PID 1 in a namespace." &lt;code&gt;tini&lt;/code&gt; exists exactly because containers need a proper PID 1.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Linux Filesystem Hierarchy
&lt;/h2&gt;

&lt;p&gt;Linux organises everything into a single tree rooted at &lt;code&gt;/&lt;/code&gt;. Unlike Windows with drive letters, there is no &lt;code&gt;C:\&lt;/code&gt; — everything is a file or a directory beneath &lt;code&gt;/&lt;/code&gt;. Understanding where things live stops you from wasting time searching for config files.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  &lt;/p&gt;
The Linux filesystem is a single tree rooted at &lt;code&gt;/&lt;/code&gt;; each top-level directory has a defined purpose per the FHS standard.





&lt;h3&gt;
  
  
  Key Directories
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Path&lt;/th&gt;
&lt;th&gt;What lives here&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;/bin&lt;/code&gt;, &lt;code&gt;/usr/bin&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Essential user binaries (&lt;code&gt;ls&lt;/code&gt;, &lt;code&gt;cp&lt;/code&gt;, &lt;code&gt;bash&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;/sbin&lt;/code&gt;, &lt;code&gt;/usr/sbin&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;System administration binaries (&lt;code&gt;fdisk&lt;/code&gt;, &lt;code&gt;iptables&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/etc&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;System-wide configuration files — everything in plain text&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/home&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;User home directories (&lt;code&gt;/home/pushkar&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/root&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Root user's home&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/var&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Variable data: logs (&lt;code&gt;/var/log&lt;/code&gt;), mail, caches, spool&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/tmp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Temporary files; cleared on reboot&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/proc&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Virtual filesystem exposing kernel and process state&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/sys&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Virtual filesystem for kernel subsystems (devices, drivers)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/dev&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Device files (&lt;code&gt;/dev/sda&lt;/code&gt;, &lt;code&gt;/dev/null&lt;/code&gt;, &lt;code&gt;/dev/random&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/boot&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Kernel image, initrd, GRUB config&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/opt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Optional third-party software&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/usr/local&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Locally compiled software (takes precedence over &lt;code&gt;/usr&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/run&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Runtime data (PIDs, sockets) — tmpfs, cleared on boot&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;/mnt&lt;/code&gt;, &lt;code&gt;/media&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Mount points for temporary / removable filesystems&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Navigating the Tree
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;                    &lt;span class="c"&gt;# print working directory&lt;/span&gt;
&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt; /etc            &lt;span class="c"&gt;# long listing including hidden files&lt;/span&gt;
&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-lh&lt;/span&gt; /var/log        &lt;span class="c"&gt;# human-readable sizes&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; /var/log            &lt;span class="c"&gt;# change directory&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; -                   &lt;span class="c"&gt;# go back to previous directory&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; ~                   &lt;span class="c"&gt;# go home&lt;/span&gt;
tree &lt;span class="nt"&gt;-L&lt;/span&gt; 2 /etc         &lt;span class="c"&gt;# visual tree, depth 2 (install with apt)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Inodes and Hard Links
&lt;/h3&gt;

&lt;p&gt;Every file is represented by an &lt;strong&gt;inode&lt;/strong&gt; — a data structure storing metadata (owner, permissions, timestamps, block pointers) but &lt;em&gt;not&lt;/em&gt; the filename. Directory entries are simply &lt;code&gt;(name → inode number)&lt;/code&gt; mappings. This is why:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hard links are just extra directory entries pointing to the same inode. Delete one, the data survives until the last link is removed.&lt;/li&gt;
&lt;li&gt;Symbolic links (&lt;code&gt;ln -s&lt;/code&gt;) store a path string. They can cross filesystem boundaries; hard links cannot.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;df -i&lt;/code&gt; shows inode usage — a filesystem can run out of inodes before running out of blocks if you create millions of tiny files.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-li&lt;/span&gt; /etc/hostname        &lt;span class="c"&gt;# shows inode number in first column&lt;/span&gt;
&lt;span class="nb"&gt;stat&lt;/span&gt; /etc/hostname          &lt;span class="c"&gt;# full inode metadata dump&lt;/span&gt;
&lt;span class="nb"&gt;ln&lt;/span&gt; /etc/hostname /tmp/hn    &lt;span class="c"&gt;# hard link (same inode)&lt;/span&gt;
&lt;span class="nb"&gt;ln&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; /etc/hostname /tmp/hn_sym   &lt;span class="c"&gt;# symbolic link&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; &lt;code&gt;/proc&lt;/code&gt; and &lt;code&gt;/sys&lt;/code&gt; contain no data on disk — they are windows into kernel memory. &lt;code&gt;cat /proc/cpuinfo&lt;/code&gt; reads CPU registers; &lt;code&gt;cat /proc/$(pgrep nginx)/status&lt;/code&gt; reads a live process's memory map. Learn to explore them freely.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  File Directory Operations
&lt;/h2&gt;

&lt;p&gt;Confident file manipulation — copying, moving, finding, archiving — is the foundation of every shell workflow.&lt;/p&gt;

&lt;h3&gt;
  
  
  Essential Commands
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create&lt;/span&gt;
&lt;span class="nb"&gt;touch &lt;/span&gt;notes.txt              &lt;span class="c"&gt;# create empty file / update mtime&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; projects/web/src    &lt;span class="c"&gt;# create directory tree in one shot&lt;/span&gt;

&lt;span class="c"&gt;# Copy, Move, Delete&lt;/span&gt;
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; /etc/nginx /tmp/nginx-bak    &lt;span class="c"&gt;# recursive copy&lt;/span&gt;
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; file dest/                   &lt;span class="c"&gt;# preserve permissions + timestamps&lt;/span&gt;
&lt;span class="nb"&gt;mv &lt;/span&gt;old_name new_name               &lt;span class="c"&gt;# rename (or move)&lt;/span&gt;
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /tmp/scratch                &lt;span class="c"&gt;# recursive force delete — no undo!&lt;/span&gt;

&lt;span class="c"&gt;# View&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; /etc/hostname           &lt;span class="c"&gt;# dump whole file&lt;/span&gt;
less /var/log/syslog        &lt;span class="c"&gt;# paged viewer (q to quit, / to search)&lt;/span&gt;
&lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-20&lt;/span&gt; /var/log/auth.log  &lt;span class="c"&gt;# first 20 lines&lt;/span&gt;
&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-50&lt;/span&gt; /var/log/syslog    &lt;span class="c"&gt;# last 50 lines&lt;/span&gt;
&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; /var/log/nginx/access.log  &lt;span class="c"&gt;# follow (real-time stream)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Finding Files
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# find — the workhorse&lt;/span&gt;
find /var/log &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"*.log"&lt;/span&gt; &lt;span class="nt"&gt;-mtime&lt;/span&gt; &lt;span class="nt"&gt;-1&lt;/span&gt;          &lt;span class="c"&gt;# modified in last 24 h&lt;/span&gt;
find /home &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="nt"&gt;-size&lt;/span&gt; +100M                 &lt;span class="c"&gt;# regular files over 100 MB&lt;/span&gt;
find /etc &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"*.conf"&lt;/span&gt; &lt;span class="nt"&gt;-exec&lt;/span&gt; &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; &lt;span class="s2"&gt;"port"&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt; &lt;span class="se"&gt;\;&lt;/span&gt;   &lt;span class="c"&gt;# grep inside results&lt;/span&gt;
find /tmp &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="nt"&gt;-newer&lt;/span&gt; /etc/hostname         &lt;span class="c"&gt;# newer than reference file&lt;/span&gt;
find &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"*.pyc"&lt;/span&gt; &lt;span class="nt"&gt;-delete&lt;/span&gt;                   &lt;span class="c"&gt;# find and delete in one pass&lt;/span&gt;

&lt;span class="c"&gt;# locate — index-based, faster but stale&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;updatedb            &lt;span class="c"&gt;# rebuild index&lt;/span&gt;
locate nginx.conf        &lt;span class="c"&gt;# instant search&lt;/span&gt;

&lt;span class="c"&gt;# which / type — find an executable&lt;/span&gt;
which python3
&lt;span class="nb"&gt;type ls&lt;/span&gt;                  &lt;span class="c"&gt;# shows if alias, function, or binary&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Archives and Compression
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# tar — tape archive (most common)&lt;/span&gt;
&lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-czf&lt;/span&gt; backup.tar.gz /etc/nginx      &lt;span class="c"&gt;# create gzipped archive&lt;/span&gt;
&lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-tzf&lt;/span&gt; backup.tar.gz                 &lt;span class="c"&gt;# list contents without extracting&lt;/span&gt;
&lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-xzf&lt;/span&gt; backup.tar.gz &lt;span class="nt"&gt;-C&lt;/span&gt; /tmp/       &lt;span class="c"&gt;# extract to /tmp&lt;/span&gt;

&lt;span class="c"&gt;# gzip / bzip2 / xz&lt;/span&gt;
&lt;span class="nb"&gt;gzip &lt;/span&gt;largefile.log         &lt;span class="c"&gt;# compresses in-place → largefile.log.gz&lt;/span&gt;
&lt;span class="nb"&gt;gunzip &lt;/span&gt;largefile.log.gz    &lt;span class="c"&gt;# decompress&lt;/span&gt;
zcat largefile.log.gz      &lt;span class="c"&gt;# read without decompressing&lt;/span&gt;

&lt;span class="c"&gt;# zip (cross-platform)&lt;/span&gt;
zip &lt;span class="nt"&gt;-r&lt;/span&gt; archive.zip &lt;span class="nb"&gt;dir&lt;/span&gt;/
unzip archive.zip &lt;span class="nt"&gt;-d&lt;/span&gt; /tmp/out
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Remember tar flags as &lt;strong&gt;C&lt;/strong&gt;reate/&lt;strong&gt;e&lt;/strong&gt;X*&lt;em&gt;t&lt;/em&gt;&lt;em&gt;ract **z&lt;/em&gt;&lt;em&gt;gzip **f&lt;/em&gt;*ile: &lt;code&gt;czf&lt;/code&gt; to create, &lt;code&gt;xzf&lt;/code&gt; to extract. For bzip2 use &lt;code&gt;j&lt;/code&gt; instead of &lt;code&gt;z&lt;/code&gt;. For xz use &lt;code&gt;J&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  File Viewing and Editing
&lt;/h2&gt;

&lt;p&gt;As a DevOps engineer, 80% of your day will be spent reading logs, editing config files, and piecing together what went wrong on a server at 2 AM. Coming from React, you've used VS Code — but on a remote production server with no GUI, you have only the terminal. This section makes you fast at viewing files, slicing log data, and editing config files directly on Linux boxes.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Real world:&lt;/strong&gt; On a production Ubuntu server, when nginx is throwing 502s, you SSH in and immediately run &lt;code&gt;tail -f /var/log/nginx/error.log&lt;/code&gt;. Then you edit &lt;code&gt;/etc/nginx/nginx.conf&lt;/code&gt; with vim, run &lt;code&gt;nginx -t&lt;/code&gt; to validate, and reload. No VS Code, no GUI — just your terminal skills.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  cat — Concatenate and View
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;cat&lt;/code&gt; dumps file contents to stdout. Great for small files; avoid on huge logs (use &lt;code&gt;less&lt;/code&gt; instead).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /etc/hostname
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; /etc/nginx/nginx.conf       &lt;span class="c"&gt;# -n prefixes every line with its line number&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="nt"&gt;-A&lt;/span&gt; script.sh                   &lt;span class="c"&gt;# -A shows hidden chars: $ for newline, ^I for tab, ^M for CRLF&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;file1.txt file2.txt &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; combined.txt
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; ~/.ssh/id_rsa.pub | ssh user@server &lt;span class="s1"&gt;'cat &amp;gt;&amp;gt; ~/.ssh/authorized_keys'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; If a bash script fails with weird "command not found" errors and you wrote it on Windows, run &lt;code&gt;cat -A script.sh&lt;/code&gt;. If you see &lt;code&gt;^M$&lt;/code&gt; at end of lines, that's CRLF. Fix with &lt;code&gt;tr -d '\r' &amp;lt; script.sh &amp;gt; clean.sh&lt;/code&gt; or &lt;code&gt;dos2unix script.sh&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  tac — Reverse cat
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;tac&lt;/code&gt; prints lines in reverse order (last line first). Useful for logs when you want the newest entries on top.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;tac&lt;/span&gt; /var/log/syslog | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-20&lt;/span&gt;     &lt;span class="c"&gt;# 20 most recent syslog entries, newest first&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;tac &lt;/span&gt;access.log | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"POST /api/login"&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  less and more — The Pagers
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;less&lt;/code&gt; is the modern, more powerful pager. &lt;strong&gt;Rule:&lt;/strong&gt; always use &lt;code&gt;less&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;less /var/log/nginx/access.log     &lt;span class="c"&gt;# file is NOT loaded fully into memory — works on huge logs&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;less +F /var/log/syslog            &lt;span class="c"&gt;# open directly in follow mode&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;journalctl &lt;span class="nt"&gt;-u&lt;/span&gt; nginx | less
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Key&lt;/th&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;q&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Quit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/pattern&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Search forward&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;?pattern&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Search backward&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;n&lt;/code&gt; / &lt;code&gt;N&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Next / previous match&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;g&lt;/code&gt; / &lt;code&gt;G&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;First / last line&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;Space&lt;/code&gt; / &lt;code&gt;b&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Next / previous page&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;F&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Follow mode (like tail -f) — Ctrl+C to exit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;amp;pattern&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show ONLY lines matching pattern (filter)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  head — First N Lines
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;head&lt;/span&gt; /etc/passwd                   &lt;span class="c"&gt;# first 10 lines (default)&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; 20 access.log
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; 100 /dev/urandom | &lt;span class="nb"&gt;base64&lt;/span&gt;  &lt;span class="c"&gt;# grab first 100 BYTES of random data&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="nt"&gt;-5&lt;/span&gt; file.txt                &lt;span class="c"&gt;# everything EXCEPT last 5 lines&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  tail — Last N Lines and Follow Mode
&lt;/h3&gt;

&lt;p&gt;If there's ONE command you must master for DevOps, it's &lt;code&gt;tail -f&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;tail&lt;/span&gt; /var/log/syslog               &lt;span class="c"&gt;# last 10 lines&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; 50 /var/log/nginx/error.log
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; /var/log/nginx/access.log  &lt;span class="c"&gt;# FOLLOW: stream new lines live. Ctrl+C to stop.&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt; /var/log/app/app.log       &lt;span class="c"&gt;# -F = follow + REOPEN on rotation&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; /var/log/nginx/access.log | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"POST"&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; app.log error.log          &lt;span class="c"&gt;# follow multiple files&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; tail -f vs tail -F: Production logs are rotated daily by logrotate. If you use &lt;code&gt;tail -f&lt;/code&gt; and the file gets rotated, you'll silently stop seeing new logs. Always use &lt;strong&gt;&lt;code&gt;tail -F&lt;/code&gt;&lt;/strong&gt; for long-running tails on production. Capital F = forever.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  wc — Word/Line/Char Count
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; /var/log/syslog              &lt;span class="c"&gt;# count lines&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-w&lt;/span&gt; README.md                    &lt;span class="c"&gt;# count words&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; image.jpg                    &lt;span class="c"&gt;# count bytes&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"ERROR"&lt;/span&gt; app.log | &lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt;       &lt;span class="c"&gt;# how many errors today?&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  sort — Sort Lines
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sort &lt;/span&gt;names.txt                     &lt;span class="c"&gt;# alphabetical&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; sizes.txt                  &lt;span class="c"&gt;# NUMERIC sort&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; names.txt                  &lt;span class="c"&gt;# reverse&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-h&lt;/span&gt;                            &lt;span class="c"&gt;# human-readable: handles 1K, 2M, 3G&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;du&lt;/span&gt; &lt;span class="nt"&gt;-h&lt;/span&gt; /var/log/&lt;span class="k"&gt;*&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-h&lt;/span&gt;         &lt;span class="c"&gt;# find which logs eat disk&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt;: &lt;span class="nt"&gt;-k3&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; /etc/passwd        &lt;span class="c"&gt;# sort users by numeric UID&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; file.txt                   &lt;span class="c"&gt;# sort + dedupe&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  uniq — Remove/Count Duplicates
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Critical:&lt;/strong&gt; &lt;code&gt;uniq&lt;/code&gt; only removes &lt;em&gt;adjacent&lt;/em&gt; duplicates. You MUST &lt;code&gt;sort&lt;/code&gt; first.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sort &lt;/span&gt;ips.txt | &lt;span class="nb"&gt;uniq&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt;             &lt;span class="c"&gt;# -c prefixes each unique line with its count&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sort &lt;/span&gt;ips.txt | &lt;span class="nb"&gt;uniq&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;             &lt;span class="c"&gt;# -d only shows lines appearing MORE than once&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $1}'&lt;/span&gt; access.log | &lt;span class="nb"&gt;sort&lt;/span&gt; | &lt;span class="nb"&gt;uniq&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-rn&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt;
&lt;span class="c"&gt;# THE classic DevOps one-liner: top 10 IPs hitting your server&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Real world:&lt;/strong&gt; Spot a DDoS in 5 seconds: &lt;code&gt;awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20&lt;/code&gt;. If one IP has 50,000 requests and the next has 200, you've got a bot. Block it with &lt;code&gt;iptables&lt;/code&gt; or &lt;code&gt;ufw&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  cut, paste, tr
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;: &lt;span class="nt"&gt;-f1&lt;/span&gt; /etc/passwd            &lt;span class="c"&gt;# extract usernames (colon delimiter, field 1)&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;: &lt;span class="nt"&gt;-f1&lt;/span&gt;,3,7 /etc/passwd        &lt;span class="c"&gt;# username, UID, shell&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"2026-05-26T14:23:11"&lt;/span&gt; | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-c1-10&lt;/span&gt;   &lt;span class="c"&gt;# extract just the date&lt;/span&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;paste &lt;/span&gt;names.txt ages.txt           &lt;span class="c"&gt;# merge line-by-line with TAB separator&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;paste&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;, names.txt ages.txt &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; combined.csv

&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Hello World"&lt;/span&gt; | &lt;span class="nb"&gt;tr &lt;/span&gt;A-Z a-z    &lt;span class="c"&gt;# lowercase&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'\r'&lt;/span&gt; &amp;lt; windows_script.sh &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; linux_script.sh   &lt;span class="c"&gt;# fix CRLF line endings&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"a   b     c"&lt;/span&gt; | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s1"&gt;' '&lt;/span&gt;     &lt;span class="c"&gt;# squeeze repeated spaces&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  tee — Write to File AND stdout
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt; | &lt;span class="nb"&gt;tee &lt;/span&gt;listing.txt           &lt;span class="c"&gt;# see output AND save it&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"new line"&lt;/span&gt; | &lt;span class="nb"&gt;tee&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; log.txt   &lt;span class="c"&gt;# -a append&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"127.0.0.1 myapp.local"&lt;/span&gt; | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; /etc/hosts
&lt;span class="nv"&gt;$ &lt;/span&gt;make 2&amp;gt;&amp;amp;1 | &lt;span class="nb"&gt;tee &lt;/span&gt;build.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; sudo tee: Bash redirection (&lt;code&gt;&amp;gt;&lt;/code&gt;) happens in the calling shell BEFORE sudo runs. So &lt;code&gt;sudo echo "x" &amp;gt; /etc/protected&lt;/code&gt; fails with "Permission denied." Use &lt;code&gt;echo "x" | sudo tee /etc/protected&lt;/code&gt; — tee runs under sudo and CAN write.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Text Editor: nano (beginner-friendly)
&lt;/h3&gt;

&lt;p&gt;All shortcuts are visible at the bottom of the screen. Good for quick edits to &lt;code&gt;/etc/hosts&lt;/code&gt; or a config file.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Shortcut&lt;/th&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Ctrl+O&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Save (Write Out)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Ctrl+X&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Exit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Ctrl+W&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Where-is — search forward&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Ctrl+\&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Search &amp;amp; replace&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;Ctrl+K&lt;/code&gt; / &lt;code&gt;Ctrl+U&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Cut line / paste&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;Alt+/&lt;/code&gt; or &lt;code&gt;Ctrl+_&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Go to line number&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Text Editor: vim — Deep Dive
&lt;/h3&gt;

&lt;p&gt;Vim is on every Linux server by default. Mastering it makes you 10x faster than someone fumbling with nano.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Four Modes:&lt;/strong&gt; Normal (navigate, delete, copy — keys are COMMANDS), Insert (type text — enter via &lt;code&gt;i&lt;/code&gt;/&lt;code&gt;a&lt;/code&gt;/&lt;code&gt;o&lt;/code&gt;, exit via &lt;code&gt;Esc&lt;/code&gt;), Visual (select text via &lt;code&gt;v&lt;/code&gt;/&lt;code&gt;V&lt;/code&gt;/&lt;code&gt;Ctrl+V&lt;/code&gt;), Command (run commands via &lt;code&gt;:&lt;/code&gt;).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Interview tip:&lt;/strong&gt; Most Asked Vim Question: "What are the vim modes and how do you exit vim?" Answer: 4 modes — Normal, Insert, Visual, Command. To exit: press &lt;code&gt;Esc&lt;/code&gt; to ensure you're in Normal mode, then type &lt;code&gt;:q&lt;/code&gt; (quit), &lt;code&gt;:wq&lt;/code&gt; (write and quit), or &lt;code&gt;:q!&lt;/code&gt; (force quit without saving).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;:w&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Save (write)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;:q&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Quit (errors if unsaved)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;:q!&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Force quit, discard changes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;:wq&lt;/code&gt; or &lt;code&gt;ZZ&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Save and quit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;i&lt;/code&gt; / &lt;code&gt;a&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Insert before / append after cursor&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;o&lt;/code&gt; / &lt;code&gt;O&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Open new line below / above&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;h j k l&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Left, Down, Up, Right&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;w&lt;/code&gt; / &lt;code&gt;b&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Next / previous word&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;0&lt;/code&gt; / &lt;code&gt;$&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Start / end of line&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;gg&lt;/code&gt; / &lt;code&gt;G&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Top / bottom of file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;dd&lt;/code&gt; / &lt;code&gt;yy&lt;/code&gt; / &lt;code&gt;p&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Delete line / yank line / paste&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;u&lt;/code&gt; / &lt;code&gt;Ctrl+r&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Undo / redo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;/pattern&lt;/code&gt; / &lt;code&gt;n&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Search / next match&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;:%s/old/new/g&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Replace ALL "old" with "new" in file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;:%s/old/new/gc&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Same, but confirm each&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A solid starter &lt;code&gt;~/.vimrc&lt;/code&gt; for DevOps work:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;syntax on
&lt;span class="nb"&gt;set &lt;/span&gt;number
&lt;span class="nb"&gt;set &lt;/span&gt;relativenumber
&lt;span class="nb"&gt;set &lt;/span&gt;expandtab          &lt;span class="s2"&gt;" convert tabs to spaces (critical for YAML/Python)
set tabstop=4
set shiftwidth=4
set hlsearch
set incsearch
set ignorecase
set smartcase
filetype plugin indent on
"&lt;/span&gt; YAML-specific &lt;span class="o"&gt;(&lt;/span&gt;2-space indent — important &lt;span class="k"&gt;for &lt;/span&gt;k8s/Ansible&lt;span class="o"&gt;)&lt;/span&gt;
autocmd FileType yaml setlocal &lt;span class="nv"&gt;ts&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2 &lt;span class="nv"&gt;sts&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2 &lt;span class="nv"&gt;sw&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2 expandtab
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Run &lt;code&gt;vimtutor&lt;/code&gt; in your terminal right now. It's a 30-minute interactive tutorial that ships with vim. By the end, you'll know enough to edit any file confidently.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Quick Reference Cheatsheet
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task&lt;/th&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;View small file&lt;/td&gt;
&lt;td&gt;&lt;code&gt;cat file&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;View large file&lt;/td&gt;
&lt;td&gt;&lt;code&gt;less file&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stream live log&lt;/td&gt;
&lt;td&gt;&lt;code&gt;tail -F /var/log/app.log&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;First/last N lines&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;head -n 20&lt;/code&gt; / &lt;code&gt;tail -n 20&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Count lines&lt;/td&gt;
&lt;td&gt;&lt;code&gt;wc -l file&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sort numerically&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sort -n&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unique with counts&lt;/td&gt;
&lt;td&gt;`sort \&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Extract column N&lt;/td&gt;
&lt;td&gt;{% raw %}&lt;code&gt;cut -d: -f N&lt;/code&gt; or &lt;code&gt;awk '{print $N}'&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Write to protected file&lt;/td&gt;
&lt;td&gt;`... \&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Power edit&lt;/td&gt;
&lt;td&gt;{% raw %}&lt;code&gt;vim file&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Exit vim (most asked)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Esc&lt;/code&gt; then &lt;code&gt;:wq&lt;/code&gt; (save) or &lt;code&gt;:q!&lt;/code&gt; (discard)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  File Permissions Ownership
&lt;/h2&gt;

&lt;p&gt;Linux permissions control who can read, write, or execute every file. Getting this wrong is a security vulnerability; getting it right is foundational operations knowledge.&lt;/p&gt;

&lt;p&gt;See also: SSH Secure Shell Critical for how permissions protect SSH keys.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;
Each file has nine permission bits split into three triplets — owner, group, others — each summed to an octal digit for use with &lt;code&gt;chmod&lt;/code&gt;.





&lt;h3&gt;
  
  
  The Permission Bits
&lt;/h3&gt;

&lt;p&gt;Every file has three permission triplets: &lt;strong&gt;owner (u)&lt;/strong&gt;, &lt;strong&gt;group (g)&lt;/strong&gt;, &lt;strong&gt;others (o)&lt;/strong&gt;. Each triplet has three bits: &lt;strong&gt;r&lt;/strong&gt; (read=4), &lt;strong&gt;w&lt;/strong&gt; (write=2), &lt;strong&gt;x&lt;/strong&gt; (execute=1).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-rwxr-xr--  1 pushkar devops 4096 Jan 1 12:00 script.sh
│└┬┘└┬┘└┬┘
│ │  │  └── others: r-- = 4 (read only)
│ │  └───── group:  r-x = 5 (read + execute)
│ └──────── owner:  rwx = 7 (full)
└────────── file type: - = regular, d = directory, l = symlink
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  chmod and chown
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Symbolic mode&lt;/span&gt;
&lt;span class="nb"&gt;chmod &lt;/span&gt;u+x script.sh          &lt;span class="c"&gt;# add execute for owner&lt;/span&gt;
&lt;span class="nb"&gt;chmod &lt;/span&gt;go-w sensitive.conf    &lt;span class="c"&gt;# remove write for group and others&lt;/span&gt;
&lt;span class="nb"&gt;chmod &lt;/span&gt;a+r public.html        &lt;span class="c"&gt;# add read for everyone&lt;/span&gt;
&lt;span class="nb"&gt;chmod &lt;/span&gt;&lt;span class="nv"&gt;u&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;rwx,g&lt;span class="o"&gt;=&lt;/span&gt;rx,o&lt;span class="o"&gt;=&lt;/span&gt; priv/   &lt;span class="c"&gt;# set exactly&lt;/span&gt;

&lt;span class="c"&gt;# Octal mode (faster once memorised)&lt;/span&gt;
&lt;span class="nb"&gt;chmod &lt;/span&gt;755 script.sh    &lt;span class="c"&gt;# rwxr-xr-x&lt;/span&gt;
&lt;span class="nb"&gt;chmod &lt;/span&gt;644 config.yml   &lt;span class="c"&gt;# rw-r--r--&lt;/span&gt;
&lt;span class="nb"&gt;chmod &lt;/span&gt;600 ~/.ssh/id_rsa       &lt;span class="c"&gt;# rw------- (private key requirement)&lt;/span&gt;
&lt;span class="nb"&gt;chmod &lt;/span&gt;700 ~/.ssh/             &lt;span class="c"&gt;# rwx------ (SSH dir requirement)&lt;/span&gt;
&lt;span class="nb"&gt;chmod &lt;/span&gt;777 /tmp/shared  &lt;span class="c"&gt;# rwxrwxrwx — almost always wrong&lt;/span&gt;

&lt;span class="c"&gt;# Ownership&lt;/span&gt;
&lt;span class="nb"&gt;chown &lt;/span&gt;pushkar:devops file.txt          &lt;span class="c"&gt;# change owner and group&lt;/span&gt;
&lt;span class="nb"&gt;chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; www-data:www-data /var/www/   &lt;span class="c"&gt;# recursive&lt;/span&gt;
&lt;span class="nb"&gt;chgrp &lt;/span&gt;docker /var/run/docker.sock      &lt;span class="c"&gt;# change group only&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Special Bits
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Bit&lt;/th&gt;
&lt;th&gt;On files&lt;/th&gt;
&lt;th&gt;On directories&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;setuid (4xxx)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Run as owner's UID (&lt;code&gt;-rwsr-xr-x&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;No effect&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;setgid (2xxx)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Run as owner's GID&lt;/td&gt;
&lt;td&gt;New files inherit directory's group&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;sticky (1xxx)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No effect&lt;/td&gt;
&lt;td&gt;Only owner/root can delete files (&lt;code&gt;/tmp&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chmod &lt;/span&gt;u+s /usr/bin/passwd    &lt;span class="c"&gt;# setuid — passwd needs root to modify /etc/shadow&lt;/span&gt;
&lt;span class="nb"&gt;chmod &lt;/span&gt;g+s /var/www/uploads   &lt;span class="c"&gt;# setgid — all new files owned by www-data group&lt;/span&gt;
&lt;span class="nb"&gt;chmod&lt;/span&gt; +t /tmp                &lt;span class="c"&gt;# sticky — standard on /tmp&lt;/span&gt;
&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt; /usr/bin/passwd       &lt;span class="c"&gt;# -rwsr-xr-x (note the 's')&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  umask
&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;umask&lt;/strong&gt; is subtracted from the default permissions when new files are created (default 666 for files, 777 for dirs).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;umask&lt;/span&gt;          &lt;span class="c"&gt;# 0022 → files get 644, dirs get 755&lt;/span&gt;
&lt;span class="nb"&gt;umask &lt;/span&gt;027      &lt;span class="c"&gt;# more restrictive: files 640, dirs 750&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; ACLs (&lt;code&gt;setfacl&lt;/code&gt;/&lt;code&gt;getfacl&lt;/code&gt;) extend beyond the three-owner model. Use them when you need fine-grained per-user access to a shared directory without changing ownership.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Grep Pattern Searching
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;grep&lt;/code&gt; filters lines matching a regular expression. It's your first tool for searching logs, configs, and code at the command line. See also Piping Redirection for how grep composes with other tools. Build and test patterns first with the &lt;a href="https://opscanopy.com/regex-log-tester/" rel="noopener noreferrer"&gt;Regex Log Tester&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Basic Usage
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"error"&lt;/span&gt; /var/log/syslog               &lt;span class="c"&gt;# case-sensitive match&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"error"&lt;/span&gt; /var/log/syslog            &lt;span class="c"&gt;# case-insensitive&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"timeout"&lt;/span&gt; app.log                  &lt;span class="c"&gt;# show line numbers&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"WARN"&lt;/span&gt; app.log                     &lt;span class="c"&gt;# count matching lines&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="s2"&gt;"DEBUG"&lt;/span&gt; app.log                    &lt;span class="c"&gt;# invert — lines NOT matching&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s2"&gt;"SECRET_KEY"&lt;/span&gt; /etc/                 &lt;span class="c"&gt;# recursive directory search&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; &lt;span class="s2"&gt;"nginx"&lt;/span&gt; /etc/logrotate.d/&lt;span class="k"&gt;*&lt;/span&gt;         &lt;span class="c"&gt;# only print filenames&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s2"&gt;"root"&lt;/span&gt; /etc/passwd                 &lt;span class="c"&gt;# whole-word match only&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Extended Regex (ERE)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s2"&gt;"error|warn|crit"&lt;/span&gt; syslog           &lt;span class="c"&gt;# alternation&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s2"&gt;"^[0-9]{4}-[0-9]{2}"&lt;/span&gt; app.log      &lt;span class="c"&gt;# lines starting with ISO date&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s2"&gt;"failed (password|publickey)"&lt;/span&gt; /var/log/auth.log  &lt;span class="c"&gt;# auth failures&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s2"&gt;"[0-9]{1,3}&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="s2"&gt;[0-9]{1,3}&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="s2"&gt;[0-9]{1,3}&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="s2"&gt;[0-9]{1,3}"&lt;/span&gt; access.log  &lt;span class="c"&gt;# IPs&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Context and Output
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-A&lt;/span&gt; 3 &lt;span class="s2"&gt;"FATAL"&lt;/span&gt; app.log    &lt;span class="c"&gt;# 3 lines after each match&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-B&lt;/span&gt; 2 &lt;span class="s2"&gt;"FATAL"&lt;/span&gt; app.log    &lt;span class="c"&gt;# 2 lines before&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-C&lt;/span&gt; 5 &lt;span class="s2"&gt;"FATAL"&lt;/span&gt; app.log    &lt;span class="c"&gt;# 5 lines before and after&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;--color&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;auto &lt;span class="s2"&gt;"error"&lt;/span&gt; syslog    &lt;span class="c"&gt;# highlight matches&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="s2"&gt;"[0-9]&lt;/span&gt;&lt;span class="se"&gt;\+\.&lt;/span&gt;&lt;span class="s2"&gt;[0-9]&lt;/span&gt;&lt;span class="se"&gt;\+&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; file     &lt;span class="c"&gt;# print only the matching portion&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Real-World Patterns
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Find all failed SSH logins&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"Failed password"&lt;/span&gt; /var/log/auth.log | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $11}'&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; | &lt;span class="nb"&gt;uniq&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-rn&lt;/span&gt;

&lt;span class="c"&gt;# Count HTTP 5xx errors in nginx log&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s1"&gt;'" 5[0-9]{2} '&lt;/span&gt; /var/log/nginx/access.log | &lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt;

&lt;span class="c"&gt;# Find config files containing a port number&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-rn&lt;/span&gt; &lt;span class="s2"&gt;"^Listen&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;^Port&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;^port"&lt;/span&gt; /etc/nginx/ /etc/apache2/ 2&amp;gt;/dev/null
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Awk Text Processing Powerhouse
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;awk&lt;/code&gt; is a field-oriented text processor. Think of it as a per-line program: for each line, split into fields, run your script. It handles 80% of the log-parsing and report-generation tasks you'd otherwise reach for Python for.&lt;/p&gt;

&lt;h3&gt;
  
  
  Basic Syntax
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $1}'&lt;/span&gt; file.txt          &lt;span class="c"&gt;# print first field (whitespace-delimited)&lt;/span&gt;
&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $1, $3}'&lt;/span&gt; file.txt      &lt;span class="c"&gt;# fields 1 and 3&lt;/span&gt;
&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print NR, $0}'&lt;/span&gt; file.txt      &lt;span class="c"&gt;# NR = record (line) number, $0 = whole line&lt;/span&gt;
&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'NR==5'&lt;/span&gt; file.txt               &lt;span class="c"&gt;# print only line 5&lt;/span&gt;
&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'NF &amp;gt; 3'&lt;/span&gt; file.txt              &lt;span class="c"&gt;# lines with more than 3 fields&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Field Separators
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;: &lt;span class="s1"&gt;'{print $1, $3}'&lt;/span&gt; /etc/passwd         &lt;span class="c"&gt;# colon-delimited&lt;/span&gt;
&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;&lt;span class="s1"&gt;'\t'&lt;/span&gt; &lt;span class="s1"&gt;'{print $2}'&lt;/span&gt; data.tsv             &lt;span class="c"&gt;# tab-delimited&lt;/span&gt;
&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'BEGIN{FS=","} {print $1}'&lt;/span&gt; data.csv      &lt;span class="c"&gt;# set FS in BEGIN block&lt;/span&gt;
&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;&lt;span class="s1"&gt;'[,;]'&lt;/span&gt; &lt;span class="s1"&gt;'{print $1}'&lt;/span&gt; mixed.txt          &lt;span class="c"&gt;# regex separator&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Built-in Variables
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Variable&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Entire current line&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;$1&lt;/code&gt;, &lt;code&gt;$2&lt;/code&gt;, ...&lt;/td&gt;
&lt;td&gt;Fields 1, 2, ...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;NR&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Current record (line) number&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;NF&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Number of fields in current record&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;FS&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Input field separator (default: whitespace)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;OFS&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Output field separator (default: space)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RS&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Record separator (default: newline)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;FILENAME&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Current filename&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  BEGIN and END Blocks
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Sum a column&lt;/span&gt;
&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{sum += $3} END {print "Total:", sum}'&lt;/span&gt; sales.csv

&lt;span class="c"&gt;# Count occurrences&lt;/span&gt;
&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{count[$1]++} END {for (k in count) print count[k], k}'&lt;/span&gt; access.log

&lt;span class="c"&gt;# Print header + filtered rows&lt;/span&gt;
&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'BEGIN{print "Name,UID"} -F: $3 &amp;gt;= 1000 {print $1","$3}'&lt;/span&gt; /etc/passwd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Real-World Awk
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Extract top 5 IPs from nginx access log&lt;/span&gt;
&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $1}'&lt;/span&gt; /var/log/nginx/access.log | &lt;span class="nb"&gt;sort&lt;/span&gt; | &lt;span class="nb"&gt;uniq&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-rn&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-5&lt;/span&gt;

&lt;span class="c"&gt;# Calculate average response time (field 7 = response_time in custom log)&lt;/span&gt;
&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{sum+=$7; n++} END {printf "avg: %.2fms\n", sum/n}'&lt;/span&gt; app.log

&lt;span class="c"&gt;# Report disk usage by filesystem (parse df output)&lt;/span&gt;
&lt;span class="nb"&gt;df&lt;/span&gt; &lt;span class="nt"&gt;-hP&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'NR&amp;gt;1 {printf "%-30s %s used\n", $6, $5}'&lt;/span&gt;

&lt;span class="c"&gt;# Reformat CSV — swap columns 1 and 2&lt;/span&gt;
&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;, &lt;span class="s1"&gt;'BEGIN{OFS=","} {print $2,$1,$3}'&lt;/span&gt; data.csv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  sed — Stream Editor
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;sed&lt;/strong&gt; is a non-interactive text editor that reads input line-by-line and applies editing commands. While vim/nano are &lt;em&gt;interactive&lt;/em&gt;, sed runs in a CI/CD pipeline, in a deployment script, in a Dockerfile — anywhere you need to programmatically modify files without a human pressing keys.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Why DevOps engineers can't live without sed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CI/CD config injection:&lt;/strong&gt; swap localhost for prod URL during deploy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Templating:&lt;/strong&gt; render config files from templates with environment-specific values.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Batch find-and-replace:&lt;/strong&gt; across hundreds of files in a single command.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Log scrubbing:&lt;/strong&gt; remove PII, mask passwords before sharing logs.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Basic Syntax
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s1"&gt;'s/old/new/'&lt;/span&gt; file           &lt;span class="c"&gt;# substitute (first occurrence per line)&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s1"&gt;'s/old/new/g'&lt;/span&gt; file          &lt;span class="c"&gt;# substitute ALL occurrences (global)&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"hello world"&lt;/span&gt; | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s1"&gt;'s/world/devops/'&lt;/span&gt;
hello devops
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By default sed prints to stdout and does NOT modify the file. Use &lt;code&gt;-i&lt;/code&gt; to edit in-place.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Operations
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;s/old/new/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Substitute first match per line&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;s/old/new/g&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Substitute all matches (global)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;s/old/new/gi&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Global, case-insensitive&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/pattern/d&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Delete lines matching pattern&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;3d&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Delete line 3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;1,5d&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Delete lines 1 through 5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/pattern/p&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print matching line (usually with &lt;code&gt;-n&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/pattern/i\text&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Insert text BEFORE matching line&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/pattern/a\text&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Append text AFTER matching line&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;y/abc/xyz/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Transliterate: a→x, b→y, c→z&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/start/,/end/d&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Delete from /start/ through /end/&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  In-Place Editing
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;'s/DEBUG=true/DEBUG=false/'&lt;/span&gt; .env          &lt;span class="c"&gt;# DANGEROUS — no undo!&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt;.bak &lt;span class="s1"&gt;'s/DEBUG=true/DEBUG=false/'&lt;/span&gt; .env       &lt;span class="c"&gt;# creates .env.bak first — RECOMMENDED&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; Always use &lt;code&gt;-i.bak&lt;/code&gt; in production. A bad sed command can corrupt thousands of files instantly. The &lt;code&gt;.bak&lt;/code&gt; safety net costs nothing.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Multiple Commands and Regex
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'s/foo/bar/g'&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'s/baz/qux/g'&lt;/span&gt; file       &lt;span class="c"&gt;# chain with -e&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s1"&gt;'s/foo/bar/g; s/baz/qux/g'&lt;/span&gt; file              &lt;span class="c"&gt;# or semicolons&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s1"&gt;'s/([0-9]+)/NUMBER/'&lt;/span&gt; file                  &lt;span class="c"&gt;# -E for Extended Regex&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s1"&gt;'s/(.+)@(.+)/User: \1, Domain: \2/'&lt;/span&gt; email.txt   &lt;span class="c"&gt;# backreferences&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Real DevOps Examples
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s1"&gt;'s/localhost/prod-server.com/g'&lt;/span&gt; config.yml    &lt;span class="c"&gt;# swap hostname (preview)&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;'s/DEBUG=true/DEBUG=false/'&lt;/span&gt; .env           &lt;span class="c"&gt;# disable debug in prod&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s1"&gt;'5,10p'&lt;/span&gt; logfile                            &lt;span class="c"&gt;# print lines 5-10&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s1"&gt;'/^#/d'&lt;/span&gt; config.conf                           &lt;span class="c"&gt;# remove comment lines&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s1"&gt;'/^$/d'&lt;/span&gt; file                                  &lt;span class="c"&gt;# strip blank lines&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;'1i\#!/bin/bash'&lt;/span&gt; script.sh                 &lt;span class="c"&gt;# add shebang at top&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s1"&gt;'s/^[[:space:]]*//'&lt;/span&gt; file                      &lt;span class="c"&gt;# trim leading whitespace&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;'s|/old/path|/new/path|g'&lt;/span&gt; config           &lt;span class="c"&gt;# use | delimiter for paths&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;'/^#PermitRootLogin/s/^#//'&lt;/span&gt; /etc/ssh/sshd_config   &lt;span class="c"&gt;# uncomment a line&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;'s/\r$//'&lt;/span&gt; file.txt                         &lt;span class="c"&gt;# convert CRLF to LF&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s1"&gt;'s/(password=)[^&amp;amp;]+/\1******/g'&lt;/span&gt; app.log    &lt;span class="c"&gt;# mask passwords&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  sed vs awk — When to Use Which
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task&lt;/th&gt;
&lt;th&gt;Use&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Find and replace text&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sed&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Built for substitution&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Delete lines by pattern&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sed&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;One-liner with /pattern/d&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Column-based processing&lt;/td&gt;
&lt;td&gt;&lt;code&gt;awk&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Field-aware out of the box&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Arithmetic / aggregation&lt;/td&gt;
&lt;td&gt;&lt;code&gt;awk&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Full programming language&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Edit config files in CI/CD&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sed&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Quick, simple substitutions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Parse log columns and count&lt;/td&gt;
&lt;td&gt;&lt;code&gt;awk&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Native associative arrays&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Real world:&lt;/strong&gt; Kubernetes ConfigMap templating with sed:&lt;/p&gt;


&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"s/__ENV__/production/g"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"s/__VERSION__/v2.4.1/g"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"s|__DB_URL__|&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;|g"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    configmap.template.yaml &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; configmap.yaml
&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; configmap.yaml
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Every modern Helm chart and Kustomize patch fundamentally does this — sed is what every templating engine wraps under the hood.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Piping Redirection
&lt;/h2&gt;

&lt;p&gt;The Unix philosophy: small tools that do one thing well, composed via pipes. Mastering redirection turns individual commands into data pipelines.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  &lt;/p&gt;
Data flows left-to-right through pipes; &lt;code&gt;&amp;gt;&lt;/code&gt; redirects final stdout to a file, while &lt;code&gt;2&amp;gt;&amp;amp;1&lt;/code&gt; merges stderr into the same stream.





&lt;h3&gt;
  
  
  Redirection Operators
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# stdout redirection&lt;/span&gt;
&lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; file.txt          &lt;span class="c"&gt;# redirect stdout (overwrite)&lt;/span&gt;
&lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; file.txt         &lt;span class="c"&gt;# append stdout&lt;/span&gt;
&lt;span class="nb"&gt;command &lt;/span&gt;2&amp;gt; errors.txt       &lt;span class="c"&gt;# redirect stderr&lt;/span&gt;
&lt;span class="nb"&gt;command &lt;/span&gt;2&amp;gt;&amp;amp;1                &lt;span class="c"&gt;# merge stderr into stdout&lt;/span&gt;
&lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; file.txt 2&amp;gt;&amp;amp;1     &lt;span class="c"&gt;# both stdout and stderr to file&lt;/span&gt;
&lt;span class="nb"&gt;command&lt;/span&gt; &amp;amp;&amp;gt; file.txt         &lt;span class="c"&gt;# bash shorthand for above&lt;/span&gt;

&lt;span class="c"&gt;# stdin redirection&lt;/span&gt;
&lt;span class="nb"&gt;command&lt;/span&gt; &amp;lt; input.txt         &lt;span class="c"&gt;# feed file as stdin&lt;/span&gt;
&lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s2"&gt;"string"&lt;/span&gt;        &lt;span class="c"&gt;# here-string (bash)&lt;/span&gt;
&lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;              # heredoc
line1
line2
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="c"&gt;# /dev/null — discard&lt;/span&gt;
&lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null 2&amp;gt;&amp;amp;1   &lt;span class="c"&gt;# suppress all output&lt;/span&gt;
&lt;span class="nb"&gt;command &lt;/span&gt;2&amp;gt;/dev/null        &lt;span class="c"&gt;# suppress errors only&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Pipes
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Basic pipe: stdout of left → stdin of right&lt;/span&gt;
&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;".log"&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; /etc/passwd | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;: &lt;span class="s1"&gt;'{print $1}'&lt;/span&gt;     &lt;span class="c"&gt;# inefficient, use awk directly&lt;/span&gt;
ps aux | &lt;span class="nb"&gt;grep &lt;/span&gt;nginx | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nb"&gt;grep&lt;/span&gt;

&lt;span class="c"&gt;# xargs — build commands from stdin&lt;/span&gt;
find /var/log &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"*.log"&lt;/span&gt; | xargs &lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt;          &lt;span class="c"&gt;# count lines in all logs&lt;/span&gt;
find /tmp &lt;span class="nt"&gt;-mtime&lt;/span&gt; +7 | xargs &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt;                  &lt;span class="c"&gt;# delete old files&lt;/span&gt;
&lt;span class="nb"&gt;cat &lt;/span&gt;servers.txt | xargs &lt;span class="nt"&gt;-I&lt;/span&gt;&lt;span class="o"&gt;{}&lt;/span&gt; ssh &lt;span class="o"&gt;{}&lt;/span&gt; &lt;span class="s2"&gt;"uptime"&lt;/span&gt;       &lt;span class="c"&gt;# run on each host&lt;/span&gt;

&lt;span class="c"&gt;# tee — split: stdout goes to both pipe and file&lt;/span&gt;
&lt;span class="nb"&gt;command&lt;/span&gt; | &lt;span class="nb"&gt;tee &lt;/span&gt;output.txt | next_command             &lt;span class="c"&gt;# log and continue&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Process Substitution
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# &amp;lt;(cmd) — treat command's output as a file (bash)&lt;/span&gt;
diff &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;sort &lt;/span&gt;file1.txt&lt;span class="o"&gt;)&lt;/span&gt; &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;sort &lt;/span&gt;file2.txt&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;comm&lt;/span&gt; &lt;span class="nt"&gt;-23&lt;/span&gt; &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;sort &lt;/span&gt;list1.txt&lt;span class="o"&gt;)&lt;/span&gt; &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;sort &lt;/span&gt;list2.txt&lt;span class="o"&gt;)&lt;/span&gt;   &lt;span class="c"&gt;# lines only in list1&lt;/span&gt;

&lt;span class="c"&gt;# &amp;gt;(cmd) — feed output to command as if it were a file&lt;/span&gt;
&lt;span class="nb"&gt;tee&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; line_count.txt&lt;span class="o"&gt;)&lt;/span&gt; &amp;lt; big_file.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Practical Pipelines
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Top 10 largest files in /var&lt;/span&gt;
find /var &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="nt"&gt;-printf&lt;/span&gt; &lt;span class="s1"&gt;'%s %p\n'&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-rn&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-10&lt;/span&gt;

&lt;span class="c"&gt;# Count unique IPs in nginx log for today&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%d/%b/%Y&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; /var/log/nginx/access.log | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $1}'&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; | &lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt;

&lt;span class="c"&gt;# Show only failed systemd units&lt;/span&gt;
systemctl list-units &lt;span class="nt"&gt;--state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;failed &lt;span class="nt"&gt;--no-legend&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $1}'&lt;/span&gt;

&lt;span class="c"&gt;# Monitor a log in real time, highlight errors&lt;/span&gt;
&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; /var/log/syslog | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;--line-buffered&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s2"&gt;"error|warn|crit"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; &lt;code&gt;grep --line-buffered&lt;/code&gt; forces grep to flush after every matched line when reading from a pipe — essential for real-time monitoring. Without it, grep buffers output and you get it in chunks.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Process Basics
&lt;/h2&gt;

&lt;p&gt;In Linux, &lt;strong&gt;everything is a process&lt;/strong&gt;. When you run &lt;code&gt;ls&lt;/code&gt;, that's a process. When a Docker container starts, that's an (isolated) process. When nginx serves a request, that's a process too. To become a DevOps engineer you must understand processes deeply.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is a Process?
&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;process&lt;/strong&gt; is a running instance of a program. The program (executable file like &lt;code&gt;/usr/bin/node&lt;/code&gt;) sits on disk — when you execute it, the kernel loads it into memory, gives it a unique &lt;strong&gt;PID&lt;/strong&gt; (Process ID), assigns CPU time, memory, and file descriptors.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;PID&lt;/strong&gt; — Process ID. Unique integer. PID 1 is always &lt;code&gt;init&lt;/code&gt; / &lt;code&gt;systemd&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PPID&lt;/strong&gt; — Parent Process ID. Every process (except PID 1) has a parent.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UID / GID&lt;/strong&gt; — User and Group ID the process runs as. Determines permissions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TTY&lt;/strong&gt; — The terminal it's attached to (or &lt;code&gt;?&lt;/code&gt; for daemons).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NI&lt;/strong&gt; — Nice value (priority, -20 highest to 19 lowest).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Foreground vs Background
&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;foreground&lt;/strong&gt; process owns the terminal and blocks your shell until it exits. A &lt;strong&gt;background&lt;/strong&gt; process detaches (append &lt;code&gt;&amp;amp;&lt;/code&gt;) so the prompt returns immediately while it keeps running.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sleep &lt;/span&gt;300        &lt;span class="c"&gt;# foreground — terminal blocked&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sleep &lt;/span&gt;300 &amp;amp;      &lt;span class="c"&gt;# background — prompt returns immediately&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;1] 24817
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;jobs&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;1]+  Running                 &lt;span class="nb"&gt;sleep &lt;/span&gt;300 &amp;amp;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  fork() and exec()
&lt;/h3&gt;

&lt;p&gt;Linux creates new processes through two system calls — the famous &lt;code&gt;fork()&lt;/code&gt; + &lt;code&gt;exec()&lt;/code&gt; pair.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;fork()&lt;/strong&gt; — The parent process duplicates itself. The child gets a new PID; parent gets the child's PID, child gets 0.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;exec()&lt;/strong&gt; — The child replaces its memory image with a new program. Same PID, different code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;wait()&lt;/strong&gt; — Parent waits for child to finish and reaps its exit status.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When you type &lt;code&gt;ls&lt;/code&gt; in bash: bash forks itself, the child execs &lt;code&gt;/usr/bin/ls&lt;/code&gt;, bash waits for it to exit.&lt;/p&gt;

&lt;h3&gt;
  
  
  Process States
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;State&lt;/th&gt;
&lt;th&gt;Code&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;th&gt;When you see it&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Running&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;R&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Currently executing or runnable&lt;/td&gt;
&lt;td&gt;Active workloads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sleeping (Interruptible)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;S&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Waiting for an event (I/O, signal, timer)&lt;/td&gt;
&lt;td&gt;Most idle daemons&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Disk Sleep (Uninterruptible)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;D&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Waiting for I/O — cannot be killed even with SIGKILL&lt;/td&gt;
&lt;td&gt;NFS hang, broken disk&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stopped&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;T&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Suspended by signal (SIGSTOP / Ctrl+Z)&lt;/td&gt;
&lt;td&gt;After you hit Ctrl+Z&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Zombie&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Z&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Dead but parent hasn't reaped exit status&lt;/td&gt;
&lt;td&gt;Bad parent process&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Idle&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;I&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Idle kernel thread&lt;/td&gt;
&lt;td&gt;Kernel worker threads&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; D state is dangerous: Disk Sleep processes ignore &lt;em&gt;all&lt;/em&gt; signals including SIGKILL. If you see lots of D state processes, your disk / NFS mount is likely broken. &lt;code&gt;kill -9&lt;/code&gt; won't help.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Exploring /proc/[pid]/
&lt;/h3&gt;

&lt;p&gt;The kernel exposes every process as a directory under &lt;code&gt;/proc/&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;pgrep &lt;span class="nt"&gt;-f&lt;/span&gt; nginx | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-1&lt;/span&gt;
1842
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /proc/1842/cmdline | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="s1"&gt;'\0'&lt;/span&gt; &lt;span class="s1"&gt;' '&lt;/span&gt;
nginx: master process /usr/sbin/nginx &lt;span class="nt"&gt;-g&lt;/span&gt; daemon on&lt;span class="p"&gt;;&lt;/span&gt; master_process on&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /proc/1842/status | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-6&lt;/span&gt;
Name:   nginx
State:  S &lt;span class="o"&gt;(&lt;/span&gt;sleeping&lt;span class="o"&gt;)&lt;/span&gt;
Pid:    1842
PPid:   1
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;readlink&lt;/span&gt; /proc/1842/exe       &lt;span class="c"&gt;# path to binary&lt;/span&gt;
/usr/sbin/nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Why Processes Matter in DevOps
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Containers ARE processes.&lt;/strong&gt; A Docker container is just a regular Linux process with extra isolation provided by the kernel: &lt;strong&gt;namespaces&lt;/strong&gt; (separate views of PIDs, network, mounts), &lt;strong&gt;cgroups&lt;/strong&gt; (CPU, memory, I/O limits), &lt;strong&gt;chroot/pivot_root&lt;/strong&gt; (restricted filesystem), and &lt;strong&gt;seccomp/capabilities&lt;/strong&gt; (restricted syscalls).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Interview tip:&lt;/strong&gt; "What's the difference between a VM and a container?" — A VM virtualizes hardware and runs a full guest kernel. A container is a regular process on the host kernel with namespaces and cgroups for isolation. Container = process. VM = virtual machine.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Process Commands
&lt;/h2&gt;

&lt;p&gt;Understanding how to inspect and manage processes is essential for debugging hung services, investigating CPU/memory spikes, and safely stopping background work.&lt;/p&gt;

&lt;p&gt;See also: systemd Services for managing processes as services.&lt;/p&gt;

&lt;h3&gt;
  
  
  Viewing Processes
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ps aux                        &lt;span class="c"&gt;# all processes, BSD syntax (a=all, u=user, x=no-tty)&lt;/span&gt;
ps &lt;span class="nt"&gt;-ef&lt;/span&gt;                        &lt;span class="c"&gt;# all processes, POSIX syntax (-e=all, -f=full)&lt;/span&gt;
ps aux | &lt;span class="nb"&gt;grep &lt;/span&gt;nginx           &lt;span class="c"&gt;# filter by name&lt;/span&gt;
ps &lt;span class="nt"&gt;-p&lt;/span&gt; 1234 &lt;span class="nt"&gt;-o&lt;/span&gt; pid,ppid,comm,etime,rss   &lt;span class="c"&gt;# specific PID, custom columns&lt;/span&gt;

&lt;span class="c"&gt;# pgrep / pkill — search/signal by name&lt;/span&gt;
pgrep &lt;span class="nt"&gt;-l&lt;/span&gt; nginx                &lt;span class="c"&gt;# list PIDs matching "nginx"&lt;/span&gt;
pgrep &lt;span class="nt"&gt;-u&lt;/span&gt; www-data             &lt;span class="c"&gt;# PIDs owned by www-data&lt;/span&gt;
pkill &lt;span class="nt"&gt;-HUP&lt;/span&gt; nginx              &lt;span class="c"&gt;# send SIGHUP to all nginx processes (graceful reload)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  top and htop
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;top              &lt;span class="c"&gt;# interactive process viewer&lt;/span&gt;
                 &lt;span class="c"&gt;# Keys: k=kill, r=renice, u=filter by user, 1=per-CPU, q=quit&lt;/span&gt;
                 &lt;span class="c"&gt;# Press 'c' to show full command line&lt;/span&gt;

htop             &lt;span class="c"&gt;# enhanced viewer (apt install htop)&lt;/span&gt;
                 &lt;span class="c"&gt;# Mouse clicks work; F5=tree view, F6=sort column&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Signals
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;kill&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt;                  &lt;span class="c"&gt;# list all signals&lt;/span&gt;
&lt;span class="nb"&gt;kill &lt;/span&gt;PID                 &lt;span class="c"&gt;# send SIGTERM (15) — polite shutdown request&lt;/span&gt;
&lt;span class="nb"&gt;kill&lt;/span&gt; &lt;span class="nt"&gt;-9&lt;/span&gt; PID              &lt;span class="c"&gt;# SIGKILL — immediate termination (no cleanup)&lt;/span&gt;
&lt;span class="nb"&gt;kill&lt;/span&gt; &lt;span class="nt"&gt;-HUP&lt;/span&gt; PID            &lt;span class="c"&gt;# SIGHUP (1) — reload config (nginx, rsyslog)&lt;/span&gt;
&lt;span class="nb"&gt;kill&lt;/span&gt; &lt;span class="nt"&gt;-STOP&lt;/span&gt; PID           &lt;span class="c"&gt;# pause a process&lt;/span&gt;
&lt;span class="nb"&gt;kill&lt;/span&gt; &lt;span class="nt"&gt;-CONT&lt;/span&gt; PID           &lt;span class="c"&gt;# resume a paused process&lt;/span&gt;

&lt;span class="c"&gt;# Sending to all instances&lt;/span&gt;
killall nginx            &lt;span class="c"&gt;# by name&lt;/span&gt;
pkill &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"python worker"&lt;/span&gt; &lt;span class="c"&gt;# by full command line regex&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Signal&lt;/th&gt;
&lt;th&gt;Number&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SIGHUP&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Hangup / reload config&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SIGINT&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Keyboard interrupt (Ctrl-C)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SIGQUIT&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Quit with core dump&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SIGKILL&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;Immediate kill (uncatchable)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SIGTERM&lt;/td&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;td&gt;Graceful shutdown (default for &lt;code&gt;kill&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SIGUSR1/2&lt;/td&gt;
&lt;td&gt;10/12&lt;/td&gt;
&lt;td&gt;User-defined (app-specific)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SIGSTOP&lt;/td&gt;
&lt;td&gt;19&lt;/td&gt;
&lt;td&gt;Pause (uncatchable)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SIGCONT&lt;/td&gt;
&lt;td&gt;18&lt;/td&gt;
&lt;td&gt;Resume&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Background Jobs and nice
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Background/foreground&lt;/span&gt;
&lt;span class="nb"&gt;command&lt;/span&gt; &amp;amp;          &lt;span class="c"&gt;# start in background&lt;/span&gt;
&lt;span class="nb"&gt;jobs&lt;/span&gt;               &lt;span class="c"&gt;# list background jobs in current shell&lt;/span&gt;
&lt;span class="nb"&gt;fg&lt;/span&gt; %1              &lt;span class="c"&gt;# bring job 1 to foreground&lt;/span&gt;
&lt;span class="nb"&gt;bg&lt;/span&gt; %2              &lt;span class="c"&gt;# resume stopped job 2 in background&lt;/span&gt;
&lt;span class="nb"&gt;nohup&lt;/span&gt; ./script.sh &amp;amp;  &lt;span class="c"&gt;# immune to SIGHUP (survives shell logout)&lt;/span&gt;
&lt;span class="nb"&gt;disown&lt;/span&gt; %1          &lt;span class="c"&gt;# detach job from shell (no SIGHUP on exit)&lt;/span&gt;

&lt;span class="c"&gt;# nice / renice — CPU scheduling priority (-20=highest, 19=lowest)&lt;/span&gt;
&lt;span class="nb"&gt;nice&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; 10 ./heavy_script.sh    &lt;span class="c"&gt;# start with lower priority&lt;/span&gt;
renice 5 &lt;span class="nt"&gt;-p&lt;/span&gt; 1234                &lt;span class="c"&gt;# change running process&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  lsof and fuser
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;lsof &lt;span class="nt"&gt;-i&lt;/span&gt; :80                   &lt;span class="c"&gt;# what's listening on port 80&lt;/span&gt;
lsof &lt;span class="nt"&gt;-p&lt;/span&gt; 1234                  &lt;span class="c"&gt;# all files opened by PID&lt;/span&gt;
lsof &lt;span class="nt"&gt;-u&lt;/span&gt; www-data              &lt;span class="c"&gt;# files opened by a user&lt;/span&gt;
lsof +D /var/log              &lt;span class="c"&gt;# all processes using files under /var/log&lt;/span&gt;

fuser 80/tcp                  &lt;span class="c"&gt;# PID using port 80&lt;/span&gt;
fuser &lt;span class="nt"&gt;-k&lt;/span&gt; 80/tcp               &lt;span class="c"&gt;# kill it&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; When a deleted file is still consuming disk space, &lt;code&gt;lsof | grep deleted&lt;/code&gt; shows which process still holds the file descriptor open. Restart that process to reclaim the space.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  systemd Services
&lt;/h2&gt;

&lt;p&gt;systemd is the init system on every modern Linux distro. It manages services, mounts, timers, sockets, and devices as a dependency graph. Everything that was once a shell script in &lt;code&gt;/etc/init.d&lt;/code&gt; is now a unit file.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;
A systemd service cycles through states driven by &lt;code&gt;systemctl start/stop/restart&lt;/code&gt;; &lt;code&gt;enable/disable&lt;/code&gt; controls boot-time auto-start independently.





&lt;h3&gt;
  
  
  Essential systemctl Commands
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Service lifecycle&lt;/span&gt;
systemctl start nginx
systemctl stop nginx
systemctl restart nginx
systemctl reload nginx            &lt;span class="c"&gt;# graceful config reload (if supported)&lt;/span&gt;
systemctl status nginx            &lt;span class="c"&gt;# status + last 10 log lines&lt;/span&gt;

&lt;span class="c"&gt;# Enable/disable at boot&lt;/span&gt;
systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;nginx            &lt;span class="c"&gt;# create symlink in wants/&lt;/span&gt;
systemctl disable nginx
systemctl &lt;span class="nb"&gt;enable&lt;/span&gt; &lt;span class="nt"&gt;--now&lt;/span&gt; nginx      &lt;span class="c"&gt;# enable + start in one command&lt;/span&gt;
systemctl is-enabled nginx        &lt;span class="c"&gt;# returns enabled/disabled/static&lt;/span&gt;

&lt;span class="c"&gt;# Inspection&lt;/span&gt;
systemctl list-units &lt;span class="nt"&gt;--type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;service &lt;span class="nt"&gt;--state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;running
systemctl list-units &lt;span class="nt"&gt;--state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;failed
systemctl &lt;span class="nb"&gt;cat &lt;/span&gt;nginx               &lt;span class="c"&gt;# print unit file&lt;/span&gt;
systemctl show nginx              &lt;span class="c"&gt;# all properties&lt;/span&gt;
systemctl list-dependencies nginx &lt;span class="c"&gt;# tree of dependencies&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Unit File Structure
&lt;/h3&gt;

&lt;p&gt;Unit files live in &lt;code&gt;/etc/systemd/system/&lt;/code&gt; (your files) and &lt;code&gt;/lib/systemd/system/&lt;/code&gt; (package defaults). Your files take precedence.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="c"&gt;# /etc/systemd/system/myapp.service
&lt;/span&gt;&lt;span class="nn"&gt;[Unit]&lt;/span&gt;
&lt;span class="py"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;My Node Application&lt;/span&gt;
&lt;span class="py"&gt;Documentation&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;https://github.com/yourorg/myapp&lt;/span&gt;
&lt;span class="py"&gt;After&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;network-online.target postgresql.service&lt;/span&gt;
&lt;span class="py"&gt;Wants&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;network-online.target&lt;/span&gt;
&lt;span class="py"&gt;Requires&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;postgresql.service&lt;/span&gt;

&lt;span class="nn"&gt;[Service]&lt;/span&gt;
&lt;span class="py"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;simple&lt;/span&gt;
&lt;span class="py"&gt;User&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;myapp&lt;/span&gt;
&lt;span class="py"&gt;Group&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;myapp&lt;/span&gt;
&lt;span class="py"&gt;WorkingDirectory&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/opt/myapp&lt;/span&gt;
&lt;span class="py"&gt;EnvironmentFile&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/etc/myapp/env&lt;/span&gt;
&lt;span class="py"&gt;ExecStart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/usr/bin/node /opt/myapp/server.js&lt;/span&gt;
&lt;span class="py"&gt;ExecReload&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/bin/kill -HUP $MAINPID&lt;/span&gt;
&lt;span class="py"&gt;Restart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;on-failure&lt;/span&gt;
&lt;span class="py"&gt;RestartSec&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;5&lt;/span&gt;
&lt;span class="py"&gt;StandardOutput&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;journal&lt;/span&gt;
&lt;span class="py"&gt;StandardError&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;journal&lt;/span&gt;
&lt;span class="py"&gt;SyslogIdentifier&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;myapp&lt;/span&gt;

&lt;span class="c"&gt;# Security hardening
&lt;/span&gt;&lt;span class="py"&gt;NoNewPrivileges&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;yes&lt;/span&gt;
&lt;span class="py"&gt;ProtectSystem&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;strict&lt;/span&gt;
&lt;span class="py"&gt;ReadWritePaths&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/var/lib/myapp /var/log/myapp&lt;/span&gt;
&lt;span class="py"&gt;PrivateTmp&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;yes&lt;/span&gt;

&lt;span class="nn"&gt;[Install]&lt;/span&gt;
&lt;span class="py"&gt;WantedBy&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;multi-user.target&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# After creating/editing a unit file&lt;/span&gt;
systemctl daemon-reload          &lt;span class="c"&gt;# reload all unit files&lt;/span&gt;
systemctl &lt;span class="nb"&gt;enable&lt;/span&gt; &lt;span class="nt"&gt;--now&lt;/span&gt; myapp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  journalctl — Reading Logs
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;journalctl &lt;span class="nt"&gt;-u&lt;/span&gt; nginx                    &lt;span class="c"&gt;# all logs for nginx unit&lt;/span&gt;
journalctl &lt;span class="nt"&gt;-u&lt;/span&gt; nginx &lt;span class="nt"&gt;-f&lt;/span&gt;                 &lt;span class="c"&gt;# follow in real time&lt;/span&gt;
journalctl &lt;span class="nt"&gt;-u&lt;/span&gt; nginx &lt;span class="nt"&gt;--since&lt;/span&gt; &lt;span class="s2"&gt;"1 hour ago"&lt;/span&gt;
journalctl &lt;span class="nt"&gt;-u&lt;/span&gt; nginx &lt;span class="nt"&gt;--since&lt;/span&gt; &lt;span class="s2"&gt;"2024-01-01"&lt;/span&gt; &lt;span class="nt"&gt;--until&lt;/span&gt; &lt;span class="s2"&gt;"2024-01-02"&lt;/span&gt;
journalctl &lt;span class="nt"&gt;-u&lt;/span&gt; nginx &lt;span class="nt"&gt;-n&lt;/span&gt; 50             &lt;span class="c"&gt;# last 50 lines&lt;/span&gt;
journalctl &lt;span class="nt"&gt;-p&lt;/span&gt; err &lt;span class="nt"&gt;-u&lt;/span&gt; nginx            &lt;span class="c"&gt;# only priority error and above&lt;/span&gt;
journalctl &lt;span class="nt"&gt;-b&lt;/span&gt;                         &lt;span class="c"&gt;# since last boot&lt;/span&gt;
journalctl &lt;span class="nt"&gt;-b&lt;/span&gt; &lt;span class="nt"&gt;-1&lt;/span&gt;                      &lt;span class="c"&gt;# previous boot&lt;/span&gt;
journalctl &lt;span class="nt"&gt;--disk-usage&lt;/span&gt;               &lt;span class="c"&gt;# how much disk the journal uses&lt;/span&gt;
journalctl &lt;span class="nt"&gt;--vacuum-size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;200M         &lt;span class="c"&gt;# trim journal to 200 MB&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  systemd Timers
&lt;/h3&gt;

&lt;p&gt;Timers replace cron with dependency tracking, logging via journal, and precise time expressions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="c"&gt;# /etc/systemd/system/backup.timer
&lt;/span&gt;&lt;span class="nn"&gt;[Unit]&lt;/span&gt;
&lt;span class="py"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Daily backup timer&lt;/span&gt;

&lt;span class="nn"&gt;[Timer]&lt;/span&gt;
&lt;span class="py"&gt;OnCalendar&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;*-*-* 02:00:00   # every day at 02:00&lt;/span&gt;
&lt;span class="py"&gt;Persistent&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true              # run if missed while system was off&lt;/span&gt;
&lt;span class="py"&gt;RandomizedDelaySec&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;10min     # spread load across fleet&lt;/span&gt;

&lt;span class="nn"&gt;[Install]&lt;/span&gt;
&lt;span class="py"&gt;WantedBy&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;timers.target&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;systemctl &lt;span class="nb"&gt;enable&lt;/span&gt; &lt;span class="nt"&gt;--now&lt;/span&gt; backup.timer
systemctl list-timers                 &lt;span class="c"&gt;# all timers + next trigger time&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Always run &lt;code&gt;systemctl daemon-reload&lt;/code&gt; after editing any unit file. Without it, systemd uses the cached version from memory and your changes are silently ignored.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  User Management
&lt;/h2&gt;

&lt;p&gt;User management is the foundation of Linux security. As a DevOps engineer, you'll create service accounts for CI/CD pipelines, manage developer access, lock down compromised users, and configure sudo policies. Think of this like managing IAM roles in AWS — except it's text files all the way down, and a single typo in &lt;code&gt;/etc/sudoers&lt;/code&gt; can lock you out of your own server.&lt;/p&gt;

&lt;h3&gt;
  
  
  /etc/passwd and /etc/shadow
&lt;/h3&gt;

&lt;p&gt;Every user account is defined in &lt;code&gt;/etc/passwd&lt;/code&gt;. Despite the name, passwords are NOT stored here — they moved to &lt;code&gt;/etc/shadow&lt;/code&gt; because &lt;code&gt;/etc/passwd&lt;/code&gt; must be world-readable.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;/etc/passwd&lt;/code&gt; has 7 colon-separated fields: &lt;code&gt;username:x:UID:GID:GECOS:home_dir:login_shell&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;grep &lt;/span&gt;deploy /etc/passwd
deploy:x:1001:1001:Deploy User,,,:/home/deploy:/bin/bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;deploy&lt;/strong&gt; — username&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;x&lt;/strong&gt; — placeholder (the &lt;code&gt;x&lt;/code&gt; means "look in /etc/shadow")&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;1001&lt;/strong&gt; — UID. 0 is root. 1-999 system users. 1000+ regular humans&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;1001&lt;/strong&gt; — primary GID&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;/home/deploy&lt;/strong&gt; — home directory&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;/bin/bash&lt;/strong&gt; — login shell (set to &lt;code&gt;/usr/sbin/nologin&lt;/code&gt; to disable interactive login)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;/etc/shadow&lt;/code&gt; (root-only) stores password hashes plus aging policy: &lt;code&gt;username:hash:last_change:min:max:warn:inactive:expire:reserved&lt;/code&gt;. The hash prefix &lt;code&gt;$6$&lt;/code&gt; = SHA-512, &lt;code&gt;$y$&lt;/code&gt; = yescrypt (Ubuntu 24.04 default), &lt;code&gt;!&lt;/code&gt; or &lt;code&gt;*&lt;/code&gt; = locked.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating Users: useradd vs adduser
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;useradd&lt;/code&gt; — low-level, scriptable, doesn't create home dir or set password by default. Everywhere.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;adduser&lt;/code&gt; — Debian/Ubuntu's friendly wrapper. Interactive, creates home, prompts for password. NOT on RHEL.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;useradd &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; /bin/bash &lt;span class="nt"&gt;-G&lt;/span&gt; &lt;span class="nb"&gt;sudo&lt;/span&gt;,docker deploy
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;passwd deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;-m&lt;/code&gt; flag creates the home directory, &lt;code&gt;-s&lt;/code&gt; sets the shell, &lt;code&gt;-G&lt;/code&gt; adds supplementary groups. Without &lt;code&gt;-m&lt;/code&gt;, the user has no home.&lt;/p&gt;

&lt;h3&gt;
  
  
  usermod — Modify Existing Users
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;usermod &lt;span class="nt"&gt;-aG&lt;/span&gt; docker &lt;span class="nv"&gt;$USER&lt;/span&gt;        &lt;span class="c"&gt;# APPEND to group&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;usermod &lt;span class="nt"&gt;-L&lt;/span&gt; olduser              &lt;span class="c"&gt;# lock password&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;usermod &lt;span class="nt"&gt;-s&lt;/span&gt; /usr/sbin/nologin ci-runner   &lt;span class="c"&gt;# change shell&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;usermod &lt;span class="nt"&gt;-e&lt;/span&gt; 2026-12-31 contractor-jay     &lt;span class="c"&gt;# set expiry&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; The &lt;code&gt;-a&lt;/code&gt; in &lt;code&gt;-aG&lt;/code&gt; is non-negotiable. Running &lt;code&gt;sudo usermod -G docker alice&lt;/code&gt; (without &lt;code&gt;-a&lt;/code&gt;) REPLACES all of Alice's supplementary groups with just &lt;code&gt;docker&lt;/code&gt;. She loses sudo, adm, dialout — everything. Always: &lt;code&gt;usermod -aG&lt;/code&gt;. The &lt;code&gt;-a&lt;/code&gt; stands for "append".&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  passwd and chage — Password and Aging
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;passwd                    &lt;span class="c"&gt;# change YOUR own password&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;passwd alice         &lt;span class="c"&gt;# change alice's password as root&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;passwd &lt;span class="nt"&gt;-l&lt;/span&gt; olduser    &lt;span class="c"&gt;# lock account&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;passwd &lt;span class="nt"&gt;-e&lt;/span&gt; alice      &lt;span class="c"&gt;# force password change on next login&lt;/span&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;chage &lt;span class="nt"&gt;-l&lt;/span&gt; deploy      &lt;span class="c"&gt;# list aging policy&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;chage &lt;span class="nt"&gt;-M&lt;/span&gt; 90 &lt;span class="nt"&gt;-W&lt;/span&gt; 7 deploy      &lt;span class="c"&gt;# expire in 90 days, warn 7 days before&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;chage &lt;span class="nt"&gt;-d&lt;/span&gt; 0 alice     &lt;span class="c"&gt;# force password change at next login&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  su vs su -
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;su username&lt;/code&gt; — switches user, keeps your current environment.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;su - username&lt;/code&gt; — switches AND runs a login shell, loading &lt;code&gt;~/.bashrc&lt;/code&gt; and resetting environment as if you logged in fresh.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Interview tip:&lt;/strong&gt; "Difference between &lt;code&gt;sudo su&lt;/code&gt; and &lt;code&gt;su -&lt;/code&gt;?" &lt;code&gt;su -&lt;/code&gt; requires the target user's password and gives a full login shell. &lt;code&gt;sudo su&lt;/code&gt; requires YOUR password (sudo authenticates the invoker), then runs &lt;code&gt;su&lt;/code&gt; as root without a password prompt — but env vars may leak. Best practice: use &lt;code&gt;sudo -i&lt;/code&gt; for a clean interactive root login shell.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  sudo and /etc/sudoers
&lt;/h3&gt;

&lt;p&gt;When you type &lt;code&gt;sudo apt update&lt;/code&gt;: the setuid-root &lt;code&gt;sudo&lt;/code&gt; binary reads &lt;code&gt;/etc/sudoers&lt;/code&gt;, verifies your user is allowed, prompts for YOUR password (cached ~15 min), logs the action to &lt;code&gt;/var/log/auth.log&lt;/code&gt;, then executes as the target user.&lt;/p&gt;

&lt;p&gt;Sudoers rules: &lt;code&gt;user_or_%group  host=(runas_user)  commands&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;root    &lt;span class="nv"&gt;ALL&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;ALL:ALL&lt;span class="o"&gt;)&lt;/span&gt; ALL                        &lt;span class="c"&gt;# root can do anything&lt;/span&gt;
%sudo   &lt;span class="nv"&gt;ALL&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;ALL:ALL&lt;span class="o"&gt;)&lt;/span&gt; ALL                         &lt;span class="c"&gt;# sudo group (Ubuntu default)&lt;/span&gt;
%wheel  &lt;span class="nv"&gt;ALL&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;ALL&lt;span class="o"&gt;)&lt;/span&gt; ALL                             &lt;span class="c"&gt;# wheel group (RHEL)&lt;/span&gt;
deploy  &lt;span class="nv"&gt;ALL&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;root&lt;span class="o"&gt;)&lt;/span&gt; NOPASSWD: /usr/bin/systemctl restart nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; NEVER edit /etc/sudoers with &lt;code&gt;vim&lt;/code&gt; or &lt;code&gt;nano&lt;/code&gt; directly. &lt;code&gt;visudo&lt;/code&gt; validates syntax BEFORE saving. A single typo renders the file unparseable and breaks sudo for everyone — including root. Recovering requires single-user mode. Use &lt;code&gt;sudo visudo&lt;/code&gt; or drop-in files in &lt;code&gt;/etc/sudoers.d/&lt;/code&gt; (chmod 0440).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Real DevOps Examples
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create a developer account with sudo + docker&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;useradd &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; /bin/bash &lt;span class="nt"&gt;-G&lt;/span&gt; &lt;span class="nb"&gt;sudo&lt;/span&gt;,docker deploy &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;passwd deploy

&lt;span class="c"&gt;# Lock a former employee's account immediately&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;passwd &lt;span class="nt"&gt;-l&lt;/span&gt; olduser &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;usermod &lt;span class="nt"&gt;-L&lt;/span&gt; olduser &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;usermod &lt;span class="nt"&gt;-s&lt;/span&gt; /usr/sbin/nologin olduser

&lt;span class="c"&gt;# Service account for CI/CD — no shell, no home login&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;useradd &lt;span class="nt"&gt;--system&lt;/span&gt; &lt;span class="nt"&gt;--shell&lt;/span&gt; /usr/sbin/nologin &lt;span class="nt"&gt;--home-dir&lt;/span&gt; /var/lib/jenkins jenkins

&lt;span class="c"&gt;# Audit who has sudo right now&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;getent group &lt;span class="nb"&gt;sudo&lt;/span&gt;

&lt;span class="c"&gt;# Run a single command as another user&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; postgres psql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Real world:&lt;/strong&gt; CI/CD Service Account — you NEVER want interactive login:&lt;/p&gt;


&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;useradd &lt;span class="nt"&gt;--system&lt;/span&gt; &lt;span class="nt"&gt;--shell&lt;/span&gt; /usr/sbin/nologin &lt;span class="nt"&gt;--home-dir&lt;/span&gt; /var/lib/runner &lt;span class="se"&gt;\&lt;/span&gt;
       &lt;span class="nt"&gt;--create-home&lt;/span&gt; &lt;span class="nt"&gt;--comment&lt;/span&gt; &lt;span class="s2"&gt;"GitLab CI Runner"&lt;/span&gt; gitlab-runner
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;usermod &lt;span class="nt"&gt;-aG&lt;/span&gt; docker gitlab-runner
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;passwd &lt;span class="nt"&gt;-l&lt;/span&gt; gitlab-runner     &lt;span class="c"&gt;# lock password — only SSH key auth&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;&lt;code&gt;--system&lt;/code&gt; assigns a UID below 1000. &lt;code&gt;nologin&lt;/code&gt; means even if SSH keys are stolen, no shell is granted. The locked password prevents &lt;code&gt;su&lt;/code&gt; attacks. Defense-in-depth.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Group Management
&lt;/h2&gt;

&lt;p&gt;Groups are how Linux scales permissions beyond individual users. Instead of granting access to 20 developers one-by-one, you create a &lt;code&gt;developers&lt;/code&gt; group, grant the group access, and add people to the group.&lt;/p&gt;

&lt;h3&gt;
  
  
  /etc/group Structure
&lt;/h3&gt;

&lt;p&gt;Four colon-separated fields: &lt;code&gt;groupname:x:GID:member1,member2,member3&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s1"&gt;'^(sudo|docker|developers)'&lt;/span&gt; /etc/group
&lt;span class="nb"&gt;sudo&lt;/span&gt;:x:27:pushkar,alice,deploy
docker:x:998:pushkar,deploy,gitlab-runner
developers:x:1100:alice,bob,charlie
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;System groups: 0–999. User groups: 1000+. The member list shows users whose &lt;em&gt;supplementary&lt;/em&gt; group this is — a user's &lt;em&gt;primary&lt;/em&gt; group is set by the GID in &lt;code&gt;/etc/passwd&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Primary vs Supplementary Groups
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Primary group&lt;/strong&gt; — exactly ONE per user, defined by GID in &lt;code&gt;/etc/passwd&lt;/code&gt;. Files the user creates are owned by this group by default.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Supplementary groups&lt;/strong&gt; — zero or many, listed in &lt;code&gt;/etc/group&lt;/code&gt;. Grant additional permissions.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;id &lt;/span&gt;pushkar
&lt;span class="nv"&gt;uid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1000&lt;span class="o"&gt;(&lt;/span&gt;pushkar&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;gid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1000&lt;span class="o"&gt;(&lt;/span&gt;pushkar&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;groups&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1000&lt;span class="o"&gt;(&lt;/span&gt;pushkar&lt;span class="o"&gt;)&lt;/span&gt;,27&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;sudo&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;,998&lt;span class="o"&gt;(&lt;/span&gt;docker&lt;span class="o"&gt;)&lt;/span&gt;,1100&lt;span class="o"&gt;(&lt;/span&gt;developers&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Managing Groups
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;groupadd developers
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;groupadd &lt;span class="nt"&gt;-g&lt;/span&gt; 1500 deploy          &lt;span class="c"&gt;# specific GID&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;groupmod &lt;span class="nt"&gt;-n&lt;/span&gt; devops developers     &lt;span class="c"&gt;# rename&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;groupdel old-team                 &lt;span class="c"&gt;# delete (refuses if it's anyone's primary group)&lt;/span&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;gpasswd &lt;span class="nt"&gt;-a&lt;/span&gt; alice developers       &lt;span class="c"&gt;# add member&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;gpasswd &lt;span class="nt"&gt;-d&lt;/span&gt; alice developers       &lt;span class="c"&gt;# remove member&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;gpasswd &lt;span class="nt"&gt;-A&lt;/span&gt; bob developers         &lt;span class="c"&gt;# make bob a group admin&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;usermod &lt;span class="nt"&gt;-aG&lt;/span&gt; docker pushkar        &lt;span class="c"&gt;# the standard "add to group" command&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; &lt;code&gt;usermod -G&lt;/code&gt; without &lt;code&gt;-a&lt;/code&gt; is destructive — it REPLACES all supplementary groups. Always &lt;code&gt;usermod -aG&lt;/code&gt;. Mnemonic: "a is for ADD, without a it ANNIHILATES."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  newgrp — Switch Active Primary Group
&lt;/h3&gt;

&lt;p&gt;If you were just added to a group, your current shell doesn't know yet. &lt;code&gt;newgrp&lt;/code&gt; spawns a subshell with the named group as primary:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;usermod &lt;span class="nt"&gt;-aG&lt;/span&gt; docker pushkar
&lt;span class="nv"&gt;$ &lt;/span&gt;docker ps        &lt;span class="c"&gt;# permission denied — current session predates the change&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;newgrp docker    &lt;span class="c"&gt;# spawn subshell with docker group active&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker ps        &lt;span class="c"&gt;# works!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Common System Groups
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Group&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Debian/Ubuntu: members can use &lt;code&gt;sudo&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;wheel&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;RHEL/CentOS equivalent of &lt;code&gt;sudo&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run docker without sudo (effectively root — be careful)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;www-data&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Apache/Nginx run as this user&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;adm&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Read access to log files in &lt;code&gt;/var/log/&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;systemd-journal&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Read journalctl logs without sudo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;disk&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Raw disk access — DANGEROUS&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Real world:&lt;/strong&gt; Shared Deployment Directory with setgid — the setgid bit makes new files inherit the directory's group:&lt;/p&gt;


&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; root:deploy /opt/app
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo chmod&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; 2775 /opt/app       &lt;span class="c"&gt;# the 2 = setgid bit&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;g+s /opt/app
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Now any file created inside &lt;code&gt;/opt/app&lt;/code&gt; is owned by the &lt;code&gt;deploy&lt;/code&gt; group, not the creator's primary group — so all team members can read/write each other's files. This is how &lt;code&gt;/srv/web/&lt;/code&gt;, &lt;code&gt;/opt/app/&lt;/code&gt;, and shared CI artifact directories should be configured.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Package Managers
&lt;/h2&gt;

&lt;p&gt;Package managers are your software distribution layer. Each distro family has its own; as a DevOps engineer you'll touch all of them.&lt;/p&gt;

&lt;h3&gt;
  
  
  apt (Debian / Ubuntu)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Update package lists&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update

&lt;span class="c"&gt;# Install / remove&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;nginx git curl
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; nginx         &lt;span class="c"&gt;# non-interactive (scripts/CI)&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt remove nginx             &lt;span class="c"&gt;# remove but keep config&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt purge nginx              &lt;span class="c"&gt;# remove + delete config files&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt autoremove               &lt;span class="c"&gt;# remove orphaned dependencies&lt;/span&gt;

&lt;span class="c"&gt;# Upgrade&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt upgrade                  &lt;span class="c"&gt;# upgrade installed packages&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt full-upgrade             &lt;span class="c"&gt;# upgrade + handle dependency changes&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;&lt;span class="nv"&gt;DEBIAN_FRONTEND&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;noninteractive apt upgrade &lt;span class="nt"&gt;-y&lt;/span&gt;   &lt;span class="c"&gt;# in scripts&lt;/span&gt;

&lt;span class="c"&gt;# Search and inspect&lt;/span&gt;
apt search &lt;span class="s2"&gt;"text editor"&lt;/span&gt;
apt show nginx
apt list &lt;span class="nt"&gt;--installed&lt;/span&gt; | &lt;span class="nb"&gt;grep &lt;/span&gt;nginx
dpkg &lt;span class="nt"&gt;-L&lt;/span&gt; nginx                     &lt;span class="c"&gt;# list files installed by package&lt;/span&gt;
dpkg &lt;span class="nt"&gt;-S&lt;/span&gt; /usr/sbin/nginx           &lt;span class="c"&gt;# which package owns a file&lt;/span&gt;

&lt;span class="c"&gt;# Pinning — hold a package at current version&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-mark hold nginx
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-mark showhold
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  dnf / yum (RHEL / Rocky / Fedora)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install &lt;/span&gt;nginx
&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf remove nginx
&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf update
&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf search nginx
&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf info nginx
&lt;span class="nb"&gt;sudo &lt;/span&gt;rpm &lt;span class="nt"&gt;-ql&lt;/span&gt; nginx                &lt;span class="c"&gt;# list files in installed rpm&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;rpm &lt;span class="nt"&gt;-qf&lt;/span&gt; /usr/sbin/nginx      &lt;span class="c"&gt;# which rpm owns a file&lt;/span&gt;

&lt;span class="c"&gt;# Repos&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf repolist
&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf config-manager &lt;span class="nt"&gt;--enable&lt;/span&gt; epel
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Alpine apk (containers / musl-based)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;apk update
apk add nginx curl bash
apk del nginx
apk search nginx
apk info nginx

&lt;span class="c"&gt;# Minimal Dockerfile example&lt;/span&gt;
FROM alpine:3.19
RUN apk add &lt;span class="nt"&gt;--no-cache&lt;/span&gt; nginx &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /var/cache/apk/&lt;span class="k"&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; In Dockerfiles always &lt;code&gt;apt-get update &amp;amp;&amp;amp; apt-get install -y pkg&lt;/code&gt; in a single &lt;code&gt;RUN&lt;/code&gt; layer. Splitting them means the cached &lt;code&gt;update&lt;/code&gt; layer can have stale package lists when your &lt;code&gt;install&lt;/code&gt; layer is rebuilt days later — causing "package not found" errors.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Disk Management
&lt;/h2&gt;

&lt;p&gt;Disk management is one of the most frequent on-call problems in DevOps. "Server is down" usually means CPU pegged, OOM killer killing processes, or — most often — &lt;strong&gt;disk full&lt;/strong&gt;. You must be fluent with &lt;code&gt;df&lt;/code&gt;, &lt;code&gt;du&lt;/code&gt;, partitioning tools, and &lt;code&gt;/etc/fstab&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  df — Disk Free
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;df&lt;/span&gt; &lt;span class="nt"&gt;-h&lt;/span&gt;
Filesystem      Size  Used Avail Use% Mounted on
/dev/sda1        50G   25G   23G  53% /
/dev/sda2       100G   89G   11G  90% /var

&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;df&lt;/span&gt; &lt;span class="nt"&gt;-hT&lt;/span&gt;          &lt;span class="c"&gt;# show filesystem TYPE — critical for choosing resize tool&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;df&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt;           &lt;span class="c"&gt;# INODES — the silent killer&lt;/span&gt;
Filesystem      Inodes   IUsed   IFree IUse% Mounted on
/dev/sda2     52428800 52428799      1  100% /var   &amp;lt;- INODE EXHAUSTED!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  du — Disk Usage (find WHO is eating space)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;du&lt;/span&gt; &lt;span class="nt"&gt;-sh&lt;/span&gt; /var/log                          &lt;span class="c"&gt;# total size of one directory&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;du&lt;/span&gt; &lt;span class="nt"&gt;-h&lt;/span&gt; &lt;span class="nt"&gt;--max-depth&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 /var | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-h&lt;/span&gt;       &lt;span class="c"&gt;# top-level breakdown, sorted&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;du&lt;/span&gt; &lt;span class="nt"&gt;-hac&lt;/span&gt; &lt;span class="nt"&gt;--max-depth&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2 &lt;span class="nb"&gt;.&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-rh&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-20&lt;/span&gt;    &lt;span class="c"&gt;# "what's eating my disk"&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo du&lt;/span&gt; &lt;span class="nt"&gt;-ah&lt;/span&gt; / 2&amp;gt;/dev/null | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-rh&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-20&lt;/span&gt;  &lt;span class="c"&gt;# whole system scan (slow)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Partitioning: fdisk and parted
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;fdisk &lt;span class="nt"&gt;-l&lt;/span&gt;                  &lt;span class="c"&gt;# list all disks and partitions&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;fdisk /dev/sdb            &lt;span class="c"&gt;# interactive (n=new, p=print, d=delete, w=WRITE)&lt;/span&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;parted &lt;span class="nt"&gt;-l&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;parted /dev/sdb mklabel gpt
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;parted /dev/sdb mkpart primary ext4 0% 100%
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;gdisk&lt;/code&gt; is the GPT-only cousin of fdisk. Use it when fdisk complains about a GPT disk.&lt;/p&gt;

&lt;h3&gt;
  
  
  lsblk and blkid — Inspection
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;lsblk            &lt;span class="c"&gt;# tree of block devices and mountpoints&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;lsblk &lt;span class="nt"&gt;-f&lt;/span&gt;         &lt;span class="c"&gt;# with FS type and UUID — best "what's on this disk?" command&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;blkid       &lt;span class="c"&gt;# UUIDs for fstab entries&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  mount / umount
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;mount /dev/sdb1 /mnt/data
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;mount &lt;span class="nt"&gt;-t&lt;/span&gt; ext4 /dev/sdb1 /mnt/data        &lt;span class="c"&gt;# specify FS type&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;mount &lt;span class="nt"&gt;-o&lt;/span&gt; ro /dev/sdb1 /mnt/data          &lt;span class="c"&gt;# read-only&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;mount &lt;span class="nt"&gt;-o&lt;/span&gt; remount,rw /                    &lt;span class="c"&gt;# remount root rw (rescue trick)&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;umount /mnt/data
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;umount &lt;span class="nt"&gt;-l&lt;/span&gt; /mnt/data                      &lt;span class="c"&gt;# lazy unmount if "device is busy"&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;findmnt                                        &lt;span class="c"&gt;# pretty tree view of all mounts&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  /etc/fstab — Persistent Mounts
&lt;/h3&gt;

&lt;p&gt;Format: &lt;code&gt;device  mountpoint  fstype  options  dump  pass&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /etc/fstab
&lt;span class="nv"&gt;UUID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;a3f2e1b4-...  /        ext4    defaults,noatime                0  1
&lt;span class="nv"&gt;UUID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;b1c2d3e4-...  /data    xfs     defaults,nofail                 0  2
tmpfs              /tmp     tmpfs   defaults,size&lt;span class="o"&gt;=&lt;/span&gt;2G,noexec         0  0
/swapfile          none     swap    sw                              0  0
nfs.internal:/exports/shared  /mnt/nfs  nfs  defaults,_netdev,soft,timeo&lt;span class="o"&gt;=&lt;/span&gt;30  0  0

&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;mount &lt;span class="nt"&gt;-a&lt;/span&gt;                &lt;span class="c"&gt;# mount everything in fstab — TEST before reboot!&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;findmnt &lt;span class="nt"&gt;--verify&lt;/span&gt;         &lt;span class="c"&gt;# sanity-check fstab without mounting&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;pass&lt;/code&gt; column controls fsck order at boot: 0=skip, 1=root only, 2=other filesystems.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; A typo in &lt;code&gt;/etc/fstab&lt;/code&gt; (wrong UUID, missing mountpoint, bad option) drops the system into emergency mode on next reboot. Always test with &lt;code&gt;sudo mount -a&lt;/code&gt; after editing. For non-critical mounts add &lt;code&gt;nofail&lt;/code&gt;; for network filesystems add &lt;code&gt;_netdev&lt;/code&gt;. To recover from a boot hang: single-user mode, &lt;code&gt;mount -o remount,rw /&lt;/code&gt;, fix fstab, reboot.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Mounting by UUID (Best Practice)
&lt;/h3&gt;

&lt;p&gt;Device names like &lt;code&gt;/dev/sdb&lt;/code&gt; can change across reboots. UUIDs are baked into the filesystem and never change. &lt;strong&gt;Always use UUIDs in fstab.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;blkid /dev/sdb1
/dev/sdb1: &lt;span class="nv"&gt;UUID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"b1c2d3e4-..."&lt;/span&gt; &lt;span class="nv"&gt;TYPE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"xfs"&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'UUID=b1c2d3e4-... /data xfs defaults,nofail 0 2'&lt;/span&gt; | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; /etc/fstab
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Swap
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;fallocate &lt;span class="nt"&gt;-l&lt;/span&gt; 2G /swapfile
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;600 /swapfile          &lt;span class="c"&gt;# required — else mkswap refuses&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;mkswap /swapfile
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;swapon /swapfile
&lt;span class="nv"&gt;$ &lt;/span&gt;swapon &lt;span class="nt"&gt;--show&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;free &lt;span class="nt"&gt;-h&lt;/span&gt;                            &lt;span class="c"&gt;# confirm swap appears&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'/swapfile none swap sw 0 0'&lt;/span&gt; | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; /etc/fstab   &lt;span class="c"&gt;# persist&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  LVM intro
&lt;/h3&gt;

&lt;p&gt;LVM (Logical Volume Manager) adds a flexible layer between partitions and filesystems: &lt;strong&gt;PV&lt;/strong&gt; (Physical Volume), &lt;strong&gt;VG&lt;/strong&gt; (Volume Group), &lt;strong&gt;LV&lt;/strong&gt; (Logical Volume). You can resize LVs without unmounting, take snapshots, and span multiple disks. Ubuntu Server defaults to LVM.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Real world:&lt;/strong&gt; "No space left on device" but &lt;code&gt;df&lt;/code&gt; shows free space? You're out of &lt;strong&gt;inodes&lt;/strong&gt;, not bytes. Every file/dir consumes one inode regardless of size. Diagnose with &lt;code&gt;df -i&lt;/code&gt; — if any IUse% is 100%, that's it. Common culprits: session files, mail queues, cache dirs with millions of tiny files. Fix: delete the small files; xfs allocates inodes dynamically and avoids this entirely.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Interview tip:&lt;/strong&gt; "Disk is full, what do you do?" (1) &lt;code&gt;df -h&lt;/code&gt; — which FS, how full. (2) &lt;code&gt;df -i&lt;/code&gt; — rule out inode exhaustion. (3) &lt;code&gt;du -h --max-depth=1 / | sort -h&lt;/code&gt; — find the heavy directory. (4) Check usual suspects: &lt;code&gt;/var/log&lt;/code&gt;, &lt;code&gt;/var/lib/docker&lt;/code&gt; (&lt;code&gt;docker system prune&lt;/code&gt;), journal (&lt;code&gt;journalctl --vacuum-size=500M&lt;/code&gt;). (5) Check deleted-but-open files: &lt;code&gt;sudo lsof | grep deleted&lt;/code&gt;. (6) Extend the volume (cloud: resize EBS → &lt;code&gt;growpart&lt;/code&gt; → &lt;code&gt;resize2fs&lt;/code&gt;/&lt;code&gt;xfs_growfs&lt;/code&gt;).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Filesystems
&lt;/h2&gt;

&lt;p&gt;A filesystem is the contract between raw blocks on a disk and the files you see. Choosing the right one matters: ext4 for general Linux, xfs for big files and parallel I/O, btrfs/zfs for snapshots, tmpfs for ephemeral RAM-backed storage, overlayfs for Docker layers.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Main Linux Filesystems
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ext4&lt;/strong&gt; — Default on Ubuntu/Debian. Mature, journaled, max file 16 TB. Resize online with &lt;code&gt;resize2fs&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;xfs&lt;/strong&gt; — Default on RHEL/Rocky/Amazon Linux. Excellent for large files and parallel I/O. Allocates inodes dynamically. Can grow online (&lt;code&gt;xfs_growfs&lt;/code&gt;) but &lt;em&gt;cannot shrink&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;btrfs&lt;/strong&gt; — Copy-on-write, snapshots, subvolumes, built-in RAID, compression. Default on Fedora Workstation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;zfs&lt;/strong&gt; — Enterprise-grade. Pools, end-to-end checksums, snapshots, send/receive replication. Not in mainline kernel (license clash) — install &lt;code&gt;zfsutils-linux&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NTFS / FAT32 / exFAT&lt;/strong&gt; — Windows/removable media. FAT32 has 4 GB per-file limit; exFAT removes it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;tmpfs&lt;/strong&gt; — RAM-backed. Files vanish on reboot. Used for &lt;code&gt;/tmp&lt;/code&gt;, &lt;code&gt;/run&lt;/code&gt;, &lt;code&gt;/dev/shm&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;overlayfs&lt;/strong&gt; — The magic behind Docker. Stacks a writable upper layer over read-only lower layers under &lt;code&gt;/var/lib/docker/overlay2/&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;proc, sysfs, devtmpfs&lt;/strong&gt; — Virtual filesystems exposing kernel state.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Comparison Table
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;ext4&lt;/th&gt;
&lt;th&gt;xfs&lt;/th&gt;
&lt;th&gt;btrfs&lt;/th&gt;
&lt;th&gt;zfs&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Max file size&lt;/td&gt;
&lt;td&gt;16 TB&lt;/td&gt;
&lt;td&gt;8 EB&lt;/td&gt;
&lt;td&gt;16 EB&lt;/td&gt;
&lt;td&gt;16 EB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Journaling&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;CoW&lt;/td&gt;
&lt;td&gt;CoW + ZIL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Copy-on-Write&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Reflinks&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Snapshots&lt;/td&gt;
&lt;td&gt;No (need LVM)&lt;/td&gt;
&lt;td&gt;No (need LVM)&lt;/td&gt;
&lt;td&gt;Native&lt;/td&gt;
&lt;td&gt;Native&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Online grow&lt;/td&gt;
&lt;td&gt;Yes (resize2fs)&lt;/td&gt;
&lt;td&gt;Yes (xfs_growfs)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Online shrink&lt;/td&gt;
&lt;td&gt;No (offline)&lt;/td&gt;
&lt;td&gt;No (never)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Built-in RAID&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes (RAID-Z)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compression&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Checksums&lt;/td&gt;
&lt;td&gt;Metadata only&lt;/td&gt;
&lt;td&gt;Metadata only&lt;/td&gt;
&lt;td&gt;Data + metadata&lt;/td&gt;
&lt;td&gt;Data + metadata&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Inode allocation&lt;/td&gt;
&lt;td&gt;Static at mkfs&lt;/td&gt;
&lt;td&gt;Dynamic&lt;/td&gt;
&lt;td&gt;Dynamic&lt;/td&gt;
&lt;td&gt;Dynamic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Default on&lt;/td&gt;
&lt;td&gt;Ubuntu, Debian&lt;/td&gt;
&lt;td&gt;RHEL, Amazon Linux&lt;/td&gt;
&lt;td&gt;Fedora WS, SUSE&lt;/td&gt;
&lt;td&gt;TrueNAS, Proxmox&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Creating and Resizing Filesystems
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;mkfs.ext4 /dev/sdb1
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;mkfs.ext4 &lt;span class="nt"&gt;-L&lt;/span&gt; data &lt;span class="nt"&gt;-m&lt;/span&gt; 1 /dev/sdb1       &lt;span class="c"&gt;# label "data", 1% reserved for root&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;mkfs.xfs &lt;span class="nt"&gt;-L&lt;/span&gt; data &lt;span class="nt"&gt;-f&lt;/span&gt; /dev/sdb1
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;mkfs.vfat &lt;span class="nt"&gt;-F&lt;/span&gt; 32 &lt;span class="nt"&gt;-n&lt;/span&gt; USB /dev/sdb1

&lt;span class="c"&gt;# ext4 grow (online or offline)&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;resize2fs /dev/sdb1            &lt;span class="c"&gt;# grow to fill partition&lt;/span&gt;
&lt;span class="c"&gt;# ext4 shrink (MUST unmount + fsck first)&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;umount /data &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;e2fsck &lt;span class="nt"&gt;-f&lt;/span&gt; /dev/sdb1 &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;resize2fs /dev/sdb1 20G
&lt;span class="c"&gt;# xfs grow ONLY (online, pass MOUNTPOINT not device)&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;xfs_growfs /data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  fsck — Filesystem Integrity
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; NEVER run fsck on a mounted filesystem — it corrupts it. Always unmount first, or boot from rescue media for the root filesystem.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;umount /dev/sdb1
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;fsck.ext4 &lt;span class="nt"&gt;-f&lt;/span&gt; /dev/sdb1          &lt;span class="c"&gt;# -f forces check even if "clean"&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;fsck &lt;span class="nt"&gt;-y&lt;/span&gt; /dev/sdb1               &lt;span class="c"&gt;# auto-answer "yes" to repairs&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;xfs_repair /dev/sdb1            &lt;span class="c"&gt;# xfs uses xfs_repair, not fsck&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;xfs_repair &lt;span class="nt"&gt;-n&lt;/span&gt; /dev/sdb1         &lt;span class="c"&gt;# dry run — report only&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Inodes — Deep Dive
&lt;/h3&gt;

&lt;p&gt;An &lt;strong&gt;inode&lt;/strong&gt; is a fixed-size record storing all metadata for a file &lt;em&gt;except&lt;/em&gt; its name: type, permissions, owner UID/GID, size, timestamps (atime/mtime/ctime), link count, and pointers to data blocks. The filename lives in the &lt;strong&gt;directory entry&lt;/strong&gt; — a (name → inode number) mapping. This single design choice explains hard links, symlinks, rename, and "deleted but still open" files.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;stat&lt;/span&gt; /etc/passwd      &lt;span class="c"&gt;# full inode metadata&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; /etc/passwd     &lt;span class="c"&gt;# show inode number&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;df&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; /               &lt;span class="c"&gt;# inode usage for a filesystem&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;File operations in terms of inodes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Rename / mv (same FS):&lt;/strong&gt; directory entry name changes; inode untouched — instant even for huge files.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hard link (&lt;code&gt;ln&lt;/code&gt;):&lt;/strong&gt; a second directory entry pointing to the &lt;em&gt;same inode&lt;/em&gt;. Link count increments. Cannot cross filesystems. Cannot link directories.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Symbolic link (&lt;code&gt;ln -s&lt;/code&gt;):&lt;/strong&gt; a tiny file with its own inode whose content is a path string. Can cross filesystems, can point to directories, can dangle.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Delete (&lt;code&gt;rm&lt;/code&gt;):&lt;/strong&gt; removes the directory entry and decrements link count. Inode and data freed only when link count reaches 0 &lt;em&gt;and&lt;/em&gt; no process has the file open.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Interview tip:&lt;/strong&gt; Hard link vs Symlink (inode perspective). &lt;strong&gt;Hard link:&lt;/strong&gt; a second directory entry pointing to the same inode — both names are first-class, same FS only, can't link directories, survives deletion of one name while link count &amp;gt; 0. &lt;strong&gt;Symlink:&lt;/strong&gt; a separate file whose data is a textual path, resolved at access time, can cross filesystems, can link directories, dangles if target removed. In &lt;code&gt;ls -l&lt;/code&gt;: a hard link looks like a normal file with link count &amp;gt; 1; a symlink shows &lt;code&gt;l&lt;/code&gt; and &lt;code&gt;-&amp;gt; target&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Real world:&lt;/strong&gt; Resizing an EBS volume on EC2 — three steps, miss one and &lt;code&gt;df&lt;/code&gt; shows no change. (1) Modify the EBS volume size in the AWS console/CLI. (2) Grow the partition: &lt;code&gt;sudo growpart /dev/nvme0n1 1&lt;/code&gt;. (3) Grow the filesystem: ext4 &lt;code&gt;sudo resize2fs /dev/nvme0n1p1&lt;/code&gt;, xfs &lt;code&gt;sudo xfs_growfs -d /&lt;/code&gt;. Entire process is online — no reboot, no unmount.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Network Commands
&lt;/h2&gt;

&lt;p&gt;Networking knowledge separates engineers who can debug production incidents from those who can't. These tools let you inspect interfaces, test connectivity, trace traffic, and diagnose service failures.&lt;/p&gt;

&lt;h3&gt;
  
  
  ip — Modern Network Configuration
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Addresses&lt;/span&gt;
ip addr show                   &lt;span class="c"&gt;# all interfaces and IPs&lt;/span&gt;
ip addr show eth0              &lt;span class="c"&gt;# specific interface&lt;/span&gt;
ip addr add 192.168.1.10/24 dev eth0   &lt;span class="c"&gt;# add IP (temporary)&lt;/span&gt;
ip addr del 192.168.1.10/24 dev eth0

&lt;span class="c"&gt;# Routes&lt;/span&gt;
ip route show                  &lt;span class="c"&gt;# routing table&lt;/span&gt;
ip route add default via 192.168.1.1   &lt;span class="c"&gt;# add default gateway&lt;/span&gt;
ip route get 8.8.8.8           &lt;span class="c"&gt;# which route is used to reach an IP&lt;/span&gt;

&lt;span class="c"&gt;# Links&lt;/span&gt;
ip &lt;span class="nb"&gt;link &lt;/span&gt;show
ip &lt;span class="nb"&gt;link set &lt;/span&gt;eth0 up
ip &lt;span class="nb"&gt;link set &lt;/span&gt;eth0 down
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ss — Socket Statistics (replaces netstat)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ss &lt;span class="nt"&gt;-tlnp&lt;/span&gt;          &lt;span class="c"&gt;# TCP (-t) listening (-l) numeric (-n) with process (-p)&lt;/span&gt;
ss &lt;span class="nt"&gt;-ulnp&lt;/span&gt;          &lt;span class="c"&gt;# UDP listening&lt;/span&gt;
ss &lt;span class="nt"&gt;-tnp&lt;/span&gt;           &lt;span class="c"&gt;# all established TCP connections&lt;/span&gt;
ss &lt;span class="nt"&gt;-s&lt;/span&gt;             &lt;span class="c"&gt;# summary statistics&lt;/span&gt;

&lt;span class="c"&gt;# Common patterns&lt;/span&gt;
ss &lt;span class="nt"&gt;-tlnp&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; :80        &lt;span class="c"&gt;# what's on port 80&lt;/span&gt;
ss &lt;span class="nt"&gt;-tnp&lt;/span&gt; state ESTABLISHED  &lt;span class="c"&gt;# only established connections&lt;/span&gt;
ss &lt;span class="nt"&gt;-tnp&lt;/span&gt; dst 10.0.0.5       &lt;span class="c"&gt;# connections to a specific host&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  dig — DNS Diagnostics
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dig example.com              &lt;span class="c"&gt;# A record (default)&lt;/span&gt;
dig example.com MX           &lt;span class="c"&gt;# mail exchange records&lt;/span&gt;
dig example.com NS           &lt;span class="c"&gt;# nameservers&lt;/span&gt;
dig example.com TXT          &lt;span class="c"&gt;# TXT records (SPF, DKIM, etc.)&lt;/span&gt;
dig @8.8.8.8 example.com    &lt;span class="c"&gt;# query specific DNS server&lt;/span&gt;
dig +short example.com       &lt;span class="c"&gt;# just the IPs&lt;/span&gt;
dig &lt;span class="nt"&gt;-x&lt;/span&gt; 93.184.216.34         &lt;span class="c"&gt;# reverse DNS lookup&lt;/span&gt;
dig +trace example.com       &lt;span class="c"&gt;# trace full resolution chain&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  curl — HTTP Testing
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://api.example.com/health          &lt;span class="c"&gt;# GET request&lt;/span&gt;
curl &lt;span class="nt"&gt;-I&lt;/span&gt; https://example.com                  &lt;span class="c"&gt;# headers only (HEAD)&lt;/span&gt;
curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"key":"value"}'&lt;/span&gt; https://api.example.com/data
curl &lt;span class="nt"&gt;-o&lt;/span&gt; /tmp/file.tar.gz https://example.com/file.tar.gz  &lt;span class="c"&gt;# download&lt;/span&gt;
curl &lt;span class="nt"&gt;-L&lt;/span&gt; https://short.url/abc               &lt;span class="c"&gt;# follow redirects&lt;/span&gt;
curl &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s2"&gt;"%{http_code} %{time_total}s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /dev/null &lt;span class="nt"&gt;-s&lt;/span&gt; https://example.com  &lt;span class="c"&gt;# timing&lt;/span&gt;
curl &lt;span class="nt"&gt;--resolve&lt;/span&gt; example.com:443:10.0.0.5 https://example.com  &lt;span class="c"&gt;# test before DNS propagation&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Other Essential Network Tools
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# ping / traceroute / mtr&lt;/span&gt;
ping &lt;span class="nt"&gt;-c&lt;/span&gt; 4 8.8.8.8
traceroute 8.8.8.8
mtr 8.8.8.8           &lt;span class="c"&gt;# real-time traceroute (apt install mtr)&lt;/span&gt;

&lt;span class="c"&gt;# nc (netcat) — the network Swiss Army knife&lt;/span&gt;
nc &lt;span class="nt"&gt;-zv&lt;/span&gt; host 22             &lt;span class="c"&gt;# port scan / test connectivity&lt;/span&gt;
nc &lt;span class="nt"&gt;-l&lt;/span&gt; 9000                 &lt;span class="c"&gt;# listen on port 9000&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"PING"&lt;/span&gt; | nc &lt;span class="nt"&gt;-u&lt;/span&gt; host 514   &lt;span class="c"&gt;# UDP test&lt;/span&gt;

&lt;span class="c"&gt;# tcpdump — packet capture&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;tcpdump &lt;span class="nt"&gt;-i&lt;/span&gt; eth0 port 80 &lt;span class="nt"&gt;-w&lt;/span&gt; capture.pcap     &lt;span class="c"&gt;# capture to file&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;tcpdump &lt;span class="nt"&gt;-i&lt;/span&gt; any host 10.0.0.5 and tcp        &lt;span class="c"&gt;# filter by host+protocol&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;tcpdump &lt;span class="nt"&gt;-r&lt;/span&gt; capture.pcap &lt;span class="nt"&gt;-A&lt;/span&gt;                  &lt;span class="c"&gt;# read and print ASCII&lt;/span&gt;

&lt;span class="c"&gt;# watch — repeat a command&lt;/span&gt;
watch &lt;span class="nt"&gt;-n&lt;/span&gt; 2 ss &lt;span class="nt"&gt;-tlnp&lt;/span&gt;        &lt;span class="c"&gt;# refresh every 2 seconds&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; &lt;code&gt;ss -tlnp&lt;/code&gt; is your first command when a service "can't start" — it shows exactly which ports are already in use and by which PID. Then &lt;code&gt;systemctl status&lt;/code&gt; that PID to find the conflict.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Network Configuration
&lt;/h2&gt;

&lt;p&gt;Commands you can run, but understanding the actual configuration files is the next level. This section covers Ubuntu 24.04's real config files — production servers, EC2 instances, on-prem bare metal all apply.&lt;/p&gt;

&lt;h3&gt;
  
  
  /etc/hosts — Local Hostname Resolution
&lt;/h3&gt;

&lt;p&gt;Consulted before DNS (per &lt;code&gt;nsswitch.conf&lt;/code&gt; order). The lifeline for local development and quick overrides.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /etc/hosts
127.0.0.1       localhost
127.0.1.1       devops-laptop
10.0.5.20       db.internal    db
10.0.5.30       redis.internal redis
::1             localhost ip6-localhost ip6-loopback
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Production cutover testing — add the new server's IP to &lt;code&gt;/etc/hosts&lt;/code&gt; and test with curl/browser &lt;em&gt;before&lt;/em&gt; changing DNS. Risk-free validation.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  /etc/resolv.conf — DNS Resolvers
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /etc/resolv.conf
nameserver 8.8.8.8
nameserver 1.1.1.1
search prod.example.com internal
options &lt;span class="nb"&gt;timeout&lt;/span&gt;:2 attempts:2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; On Ubuntu 24.04, &lt;code&gt;/etc/resolv.conf&lt;/code&gt; is usually a symlink to &lt;code&gt;/run/systemd/resolve/stub-resolv.conf&lt;/code&gt; managed by &lt;code&gt;systemd-resolved&lt;/code&gt;. Direct edits get overwritten. Use Netplan or &lt;code&gt;resolvectl&lt;/code&gt; instead:&lt;/p&gt;


&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;resolvectl status
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;resolvectl dns eth0 1.1.1.1 8.8.8.8
&lt;/code&gt;&lt;/pre&gt;

&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  /etc/nsswitch.conf — Lookup Order
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; ^hosts /etc/nsswitch.conf
hosts: files mdns4_minimal &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;NOTFOUND&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; dns
&lt;span class="c"&gt;# files → /etc/hosts first, mdns4 → .local addresses, dns → /etc/resolv.conf&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Netplan — Modern Ubuntu (Primary)
&lt;/h3&gt;

&lt;p&gt;Ubuntu 18.04+ default. YAML-based frontend for NetworkManager or systemd-networkd.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# /etc/netplan/01-static.yaml&lt;/span&gt;
&lt;span class="na"&gt;network&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
  &lt;span class="na"&gt;renderer&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;networkd&lt;/span&gt;
  &lt;span class="na"&gt;ethernets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;eth0&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;dhcp4&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
      &lt;span class="na"&gt;addresses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;192.168.1.50/24&lt;/span&gt;
      &lt;span class="na"&gt;routes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default&lt;/span&gt;
          &lt;span class="na"&gt;via&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;192.168.1.1&lt;/span&gt;
      &lt;span class="na"&gt;nameservers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;addresses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;8.8.8.8&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;1.1.1.1&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
        &lt;span class="na"&gt;search&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;prod.example.com&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;mtu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1500&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;netplan try          &lt;span class="c"&gt;# test for 120s, auto-rollback if SSH dies&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;netplan apply        &lt;span class="c"&gt;# commit changes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; YAML is strict — 2-space indentation, NO TABS. Wrong indent = network down. Always use &lt;code&gt;netplan try&lt;/code&gt; first on remote boxes.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  NetworkManager (nmcli) — Desktop/Laptop
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;nmcli device status
&lt;span class="nv"&gt;$ &lt;/span&gt;nmcli con show                              &lt;span class="c"&gt;# all profiles&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;nmcli dev wifi connect &lt;span class="s2"&gt;"MySSID"&lt;/span&gt; password &lt;span class="s2"&gt;"secret"&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;nmcli con mod eth0 ipv4.addresses 10.0.1.50/24 ipv4.gateway 10.0.1.1 ipv4.method manual
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  systemd-networkd and Hostname
&lt;/h3&gt;

&lt;p&gt;Cloud servers usually use &lt;code&gt;systemd-networkd&lt;/code&gt; (lightweight). Netplan generates its config under &lt;code&gt;/run/systemd/network/&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;networkctl status
&lt;span class="nv"&gt;$ &lt;/span&gt;hostnamectl
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;hostnamectl set-hostname prod-web-01
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; After a hostname change, also update &lt;code&gt;/etc/hosts&lt;/code&gt; (especially the &lt;code&gt;127.0.1.1&lt;/code&gt; line). Otherwise &lt;code&gt;sudo&lt;/code&gt; warns "unable to resolve host".&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Bonding and VLANs
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Netplan: active-backup bond for HA&lt;/span&gt;
&lt;span class="na"&gt;network&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
  &lt;span class="na"&gt;bonds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;bond0&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;interfaces&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;eth0&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;eth1&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;addresses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;10.0.1.50/24&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;parameters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;active-backup&lt;/span&gt;
        &lt;span class="na"&gt;primary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;eth0&lt;/span&gt;
  &lt;span class="na"&gt;ethernets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;eth0&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{}&lt;/span&gt;
    &lt;span class="na"&gt;eth1&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Common bonding modes: &lt;code&gt;active-backup&lt;/code&gt; (failover), &lt;code&gt;balance-rr&lt;/code&gt; (round robin), &lt;code&gt;802.3ad&lt;/code&gt; (LACP, needs switch support). VLANs tag one physical NIC into multiple logical networks (802.1Q).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Cloud reality check: 95% of production cloud instances (AWS EC2, GCP, Azure VM) take their IP via DHCP / cloud-init — static IPs are rarely set at the OS level because VPC subnets and ENIs handle assignment, and auto-scaling groups use dynamic IPs. But for on-prem, hybrid, edge devices, bastion hosts, and NFS servers, static IP is a must. Know both perspectives for interviews.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Interview tip:&lt;/strong&gt; "Netplan vs NetworkManager?" — Netplan is a YAML &lt;em&gt;frontend&lt;/em&gt;. The real backend is NetworkManager (desktop) or systemd-networkd (server). Ubuntu cloud images default to &lt;code&gt;renderer: networkd&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  SSH Secure Shell Critical
&lt;/h2&gt;

&lt;p&gt;SSH is how you access every cloud VM, container host, and remote server. Beyond basic login, it gives you encrypted tunnels, file transfer, and agent forwarding. Misconfiguring it creates security holes; understanding it properly lets you build zero-trust access patterns.&lt;/p&gt;

&lt;p&gt;See also: File Permissions Ownership — SSH enforces strict permission checks on key files. Going deeper on networking? See &lt;a href="https://opscanopy.com/learn/guides/networking-for-devops/" rel="noopener noreferrer"&gt;Networking for DevOps&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;
SSH key auth: the client signs a server-issued challenge with its private key; the server verifies using the stored public key — no password ever crosses the network.





&lt;h3&gt;
  
  
  Key-Based Authentication
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Generate a key pair (Ed25519 is faster and more secure than RSA)&lt;/span&gt;
ssh-keygen &lt;span class="nt"&gt;-t&lt;/span&gt; ed25519 &lt;span class="nt"&gt;-C&lt;/span&gt; &lt;span class="s2"&gt;"pushkar@work"&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; ~/.ssh/id_ed25519

&lt;span class="c"&gt;# Copy public key to server&lt;/span&gt;
ssh-copy-id &lt;span class="nt"&gt;-i&lt;/span&gt; ~/.ssh/id_ed25519.pub user@server
&lt;span class="c"&gt;# or manually:&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; ~/.ssh/id_ed25519.pub | ssh user@server &lt;span class="s1"&gt;'mkdir -p ~/.ssh &amp;amp;&amp;amp; cat &amp;gt;&amp;gt; ~/.ssh/authorized_keys'&lt;/span&gt;

&lt;span class="c"&gt;# Permission requirements (SSH will refuse with wrong perms)&lt;/span&gt;
&lt;span class="nb"&gt;chmod &lt;/span&gt;700 ~/.ssh
&lt;span class="nb"&gt;chmod &lt;/span&gt;600 ~/.ssh/id_ed25519         &lt;span class="c"&gt;# private key&lt;/span&gt;
&lt;span class="nb"&gt;chmod &lt;/span&gt;644 ~/.ssh/id_ed25519.pub     &lt;span class="c"&gt;# public key&lt;/span&gt;
&lt;span class="nb"&gt;chmod &lt;/span&gt;600 ~/.ssh/authorized_keys
&lt;span class="nb"&gt;chmod &lt;/span&gt;644 ~/.ssh/known_hosts

&lt;span class="c"&gt;# Start ssh-agent and load key&lt;/span&gt;
&lt;span class="nb"&gt;eval&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;ssh-agent &lt;span class="nt"&gt;-s&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
ssh-add ~/.ssh/id_ed25519
ssh-add &lt;span class="nt"&gt;-l&lt;/span&gt;                          &lt;span class="c"&gt;# list loaded keys&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The public key body (the long string after &lt;code&gt;ssh-ed25519&lt;/code&gt;) is base64-encoded binary. If you ever need to inspect or transform an encoded blob like this — or a base64 secret in a Kubernetes manifest — the &lt;a href="https://opscanopy.com/base64-encoder-decoder/" rel="noopener noreferrer"&gt;Base64 Encoder/Decoder&lt;/a&gt; is handy.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  ~/.ssh/config
&lt;/h3&gt;

&lt;p&gt;The client config file eliminates typing long hostnames and options every time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# ~/.ssh/config

Host bastion
    HostName 203.0.113.10
    User ec2-user
    IdentityFile ~/.ssh/aws-key.pem
    ServerAliveInterval 60

Host prod-db
    HostName 10.0.1.50             # private IP
    User ubuntu
    ProxyJump bastion              # tunnel through bastion
    IdentityFile ~/.ssh/id_ed25519

Host dev-*
    User developer
    IdentityFile ~/.ssh/dev-key
    StrictHostKeyChecking no       # only for throwaway dev VMs
    UserKnownHostsFile /dev/null
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh bastion            &lt;span class="c"&gt;# uses config above&lt;/span&gt;
ssh prod-db            &lt;span class="c"&gt;# automatically tunnels via bastion&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  SSH Tunnels
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Local port forwarding: access remote service on your local port&lt;/span&gt;
ssh &lt;span class="nt"&gt;-L&lt;/span&gt; 5432:db-host:5432 bastion     &lt;span class="c"&gt;# connect to postgresql via bastion&lt;/span&gt;
&lt;span class="c"&gt;# Now: psql -h localhost -p 5432&lt;/span&gt;

&lt;span class="c"&gt;# Remote port forwarding: expose local port on the remote server&lt;/span&gt;
ssh &lt;span class="nt"&gt;-R&lt;/span&gt; 8080:localhost:3000 server    &lt;span class="c"&gt;# server:8080 → your localhost:3000&lt;/span&gt;

&lt;span class="c"&gt;# Dynamic (SOCKS proxy): route browser traffic through the tunnel&lt;/span&gt;
ssh &lt;span class="nt"&gt;-D&lt;/span&gt; 1080 bastion                  &lt;span class="c"&gt;# configure browser to use SOCKS5 localhost:1080&lt;/span&gt;

&lt;span class="c"&gt;# Background / non-interactive&lt;/span&gt;
ssh &lt;span class="nt"&gt;-fNL&lt;/span&gt; 5432:db:5432 bastion       &lt;span class="c"&gt;# -f = background, -N = no command&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  scp and rsync
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# scp — simple copy&lt;/span&gt;
scp file.txt user@server:/tmp/
scp &lt;span class="nt"&gt;-r&lt;/span&gt; user@server:/var/www/ ./backup/
scp &lt;span class="nt"&gt;-P&lt;/span&gt; 2222 file.txt user@server:/tmp/    &lt;span class="c"&gt;# custom port&lt;/span&gt;

&lt;span class="c"&gt;# rsync — incremental sync (preferred for large transfers)&lt;/span&gt;
rsync &lt;span class="nt"&gt;-avz&lt;/span&gt; /local/dir/ user@server:/remote/dir/    &lt;span class="c"&gt;# sync to remote&lt;/span&gt;
rsync &lt;span class="nt"&gt;-avz&lt;/span&gt; &lt;span class="nt"&gt;--delete&lt;/span&gt; /src/ /dst/                     &lt;span class="c"&gt;# mirror (delete extra)&lt;/span&gt;
rsync &lt;span class="nt"&gt;-avz&lt;/span&gt; &lt;span class="nt"&gt;--exclude&lt;/span&gt; &lt;span class="s1"&gt;'.git'&lt;/span&gt; &lt;span class="nt"&gt;--exclude&lt;/span&gt; &lt;span class="s1"&gt;'node_modules'&lt;/span&gt; ./ server:/opt/app/
rsync &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"ssh -i ~/.ssh/key.pem"&lt;/span&gt; /file user@host:/dest/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  sshd — Server Configuration Hardening
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nano /etc/ssh/sshd_config
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# /etc/ssh/sshd_config (key hardening settings)
Port 2222                          # non-standard port (minor obscurity)
PermitRootLogin no                 # never allow root login
PasswordAuthentication no          # key-only (most important setting)
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys
AllowUsers pushkar deploy
MaxAuthTries 3
LoginGraceTime 20
ClientAliveInterval 300
ClientAliveCountMax 2
X11Forwarding no
AllowTcpForwarding yes             # needed for tunnels
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;sshd &lt;span class="nt"&gt;-t&lt;/span&gt;                        &lt;span class="c"&gt;# test config syntax&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl reload sshd          &lt;span class="c"&gt;# apply without dropping connections&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Always test your sshd config with &lt;code&gt;sshd -t&lt;/code&gt; and keep an existing SSH session open while applying changes. If you lock yourself out of a cloud VM, you'll need console access or a rescue snapshot.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Bash Fundamentals
&lt;/h2&gt;

&lt;p&gt;Bash (&lt;strong&gt;Bourne Again SHell&lt;/strong&gt;) is the default interactive shell on Ubuntu, Debian, RHEL derivatives, and most Linux distros. As a DevOps engineer, bash is your daily driver — every CI pipeline, every Dockerfile &lt;code&gt;RUN&lt;/code&gt;, every cron job, every &lt;code&gt;systemd&lt;/code&gt; &lt;code&gt;ExecStart&lt;/code&gt; is some flavour of shell glue.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Ubuntu's &lt;code&gt;/bin/sh&lt;/code&gt; is actually &lt;code&gt;dash&lt;/code&gt; (Debian Almquist Shell) — a POSIX-compliant shell with &lt;em&gt;no&lt;/em&gt; bash extensions. Scripts written assuming bash but invoked as &lt;code&gt;sh script.sh&lt;/code&gt; will fail on &lt;code&gt;[[ ]]&lt;/code&gt;, arrays, or &lt;code&gt;$(())&lt;/code&gt; arithmetic. Always be explicit about your shebang.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  The Shebang and Running Scripts
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash            # Absolute path — works on virtually every Linux box&lt;/span&gt;
&lt;span class="c"&gt;#!/usr/bin/env bash    # Looks up bash via PATH — portable to macOS/BSD&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Shell&lt;/th&gt;
&lt;th&gt;Needs +x?&lt;/th&gt;
&lt;th&gt;Variables persist in parent?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;./script.sh&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Subshell (from shebang)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;bash script.sh&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;New bash subshell&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;source script.sh&lt;/code&gt; or &lt;code&gt;. script.sh&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Current shell&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Variables
&lt;/h3&gt;

&lt;p&gt;The cardinal rule: &lt;strong&gt;no spaces around the &lt;code&gt;=&lt;/code&gt; sign&lt;/strong&gt;. Always quote when expanding to handle spaces and globs safely.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Pushkar"&lt;/span&gt;
&lt;span class="nv"&gt;GREETING&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Hello, &lt;/span&gt;&lt;span class="nv"&gt;$NAME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;GREETING&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;!"&lt;/span&gt;                   &lt;span class="c"&gt;# braces disambiguate variable name&lt;/span&gt;

&lt;span class="c"&gt;# Parameter expansion&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;UNSET_VAR&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;default&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;         &lt;span class="c"&gt;# use default if unset/empty&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;MAYBE&lt;/span&gt;:&lt;span class="p"&gt;=set_now&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;             &lt;span class="c"&gt;# assign default if unset (sticky)&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REQUIRED&lt;/span&gt;:?must&lt;span class="p"&gt; be set&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;      &lt;span class="c"&gt;# error and exit if unset&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;NAME&lt;/span&gt;:+exists&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;               &lt;span class="c"&gt;# expand to 'exists' only if set&lt;/span&gt;

&lt;span class="nv"&gt;FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"backup.tar.gz"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${#&lt;/span&gt;&lt;span class="nv"&gt;FILE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;                      &lt;span class="c"&gt;# 13  (length)&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;FILE&lt;/span&gt;&lt;span class="p"&gt;%.gz&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;                   &lt;span class="c"&gt;# backup.tar  (strip shortest suffix)&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;FILE&lt;/span&gt;&lt;span class="p"&gt;%%.*&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;                   &lt;span class="c"&gt;# backup       (strip longest suffix)&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;FILE&lt;/span&gt;&lt;span class="p"&gt;/tar/zip&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;               &lt;span class="c"&gt;# backup.zip.gz (replace first match)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Positional Parameters
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Variable&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Script name&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;$1&lt;/code&gt; ... &lt;code&gt;$9&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;First nine positional arguments&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$#&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Number of arguments&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;"$@"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;All args, each preserved as one quoted word&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$$&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;PID of current shell&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$?&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Exit code of last command (0 = success)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$!&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;PID of last background process&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Bash Strict Mode
&lt;/h3&gt;

&lt;p&gt;Put this at the top of every serious script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-euo&lt;/span&gt; pipefail
&lt;span class="nv"&gt;IFS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;$'&lt;/span&gt;&lt;span class="se"&gt;\n\t&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Flag&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;-e&lt;/code&gt; (errexit)&lt;/td&gt;
&lt;td&gt;Exit immediately when any command returns non-zero&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;-u&lt;/code&gt; (nounset)&lt;/td&gt;
&lt;td&gt;Treat references to unset variables as errors&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-o pipefail&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pipeline exit code is the rightmost non-zero status&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;IFS=$'\n\t'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Restrict word-splitting to newlines/tabs&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Arithmetic and Input
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="k"&gt;$((&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt;            &lt;span class="c"&gt;# 14  — recommended form&lt;/span&gt;
&lt;span class="nv"&gt;x&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5
&lt;span class="o"&gt;((&lt;/span&gt;x++&lt;span class="o"&gt;))&lt;/span&gt;                         &lt;span class="c"&gt;# x is now 6&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"scale=2; 7/3"&lt;/span&gt; | bc       &lt;span class="c"&gt;# floating point: 2.33&lt;/span&gt;

&lt;span class="nb"&gt;read&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"Your name: "&lt;/span&gt; name
&lt;span class="nb"&gt;read&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"Password: "&lt;/span&gt; pass   &lt;span class="c"&gt;# silent input&lt;/span&gt;
&lt;span class="nb"&gt;read&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; 10 &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"Quick: "&lt;/span&gt; ans    &lt;span class="c"&gt;# 10-second timeout&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Use &lt;code&gt;source&lt;/code&gt; only for shell &lt;em&gt;configuration&lt;/em&gt; files (&lt;code&gt;.bashrc&lt;/code&gt;, env loaders). Use &lt;code&gt;./script.sh&lt;/code&gt; for everything else — sourcing arbitrary scripts can pollute your interactive shell if they call &lt;code&gt;exit&lt;/code&gt; or &lt;code&gt;set -e&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Bash Control Flow
&lt;/h2&gt;

&lt;p&gt;Control flow in bash looks unfamiliar at first — &lt;code&gt;then&lt;/code&gt;, &lt;code&gt;fi&lt;/code&gt;, &lt;code&gt;do&lt;/code&gt;, &lt;code&gt;done&lt;/code&gt;, double brackets. It's because the shell parses commands by whitespace, so keywords need clear separators. Once you internalize the patterns, complex automation becomes muscle memory.&lt;/p&gt;

&lt;h3&gt;
  
  
  if / elif / else
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$USER&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"root"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Running as root"&lt;/span&gt;
&lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$USER&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"pushkar"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Hi Pushkar"&lt;/span&gt;
&lt;span class="k"&gt;else
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Unknown user: &lt;/span&gt;&lt;span class="nv"&gt;$USER&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# if on any command's exit code&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="s2"&gt;"ERROR"&lt;/span&gt; /var/log/syslog&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Errors found in syslog"&lt;/span&gt;
&lt;span class="k"&gt;fi

if &lt;/span&gt;systemctl is-active &lt;span class="nt"&gt;--quiet&lt;/span&gt; nginx&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"nginx is running"&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Use &lt;code&gt;[[ ]]&lt;/code&gt; in bash scripts always. Use &lt;code&gt;[ ]&lt;/code&gt; only when writing a portable &lt;code&gt;/bin/sh&lt;/code&gt; script. Inside &lt;code&gt;[[ ]]&lt;/code&gt;, do &lt;strong&gt;not&lt;/strong&gt; quote the right side of &lt;code&gt;=~&lt;/code&gt; — quoting turns the regex into a literal string.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Comparison Operators
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Integer (&lt;code&gt;[[ ]]&lt;/code&gt; or &lt;code&gt;(( ))&lt;/code&gt;)&lt;/th&gt;
&lt;th&gt;String (&lt;code&gt;[[ ]]&lt;/code&gt;)&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-eq&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;==&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Equal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-ne&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;!=&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Not equal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-lt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Less than&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-gt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Greater than&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-z STR&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;String is empty&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-n STR&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;String is non-empty&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;&lt;code&gt;STR =~ REGEX&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;String matches ERE regex&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  File Test Operators
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operator&lt;/th&gt;
&lt;th&gt;True if...&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-e FILE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;File exists (any type)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-f FILE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Regular file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-d DIR&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Directory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-r FILE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Readable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-w FILE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Writable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-x FILE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Executable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-s FILE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Exists and non-empty&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  case Statement
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$action&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in
    &lt;/span&gt;start&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Starting..."&lt;/span&gt;
        &lt;span class="p"&gt;;;&lt;/span&gt;
    stop|halt&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Stopping..."&lt;/span&gt;
        &lt;span class="p"&gt;;;&lt;/span&gt;
    &lt;span class="k"&gt;*&lt;/span&gt;.log&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"That looks like a log file"&lt;/span&gt;
        &lt;span class="p"&gt;;;&lt;/span&gt;
    &lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Unknown action: &lt;/span&gt;&lt;span class="nv"&gt;$action&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
        &lt;span class="nb"&gt;exit &lt;/span&gt;1
        &lt;span class="p"&gt;;;&lt;/span&gt;
&lt;span class="k"&gt;esac&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Loops
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# for-in with a list&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="nb"&gt;env &lt;/span&gt;&lt;span class="k"&gt;in &lt;/span&gt;dev staging prod&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Deploying to &lt;/span&gt;&lt;span class="nv"&gt;$env&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;

&lt;span class="c"&gt;# Brace expansion range&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;i &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;1..10&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"loop &lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;done&lt;/span&gt;

&lt;span class="c"&gt;# Glob iteration — always quote the variable&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;f &lt;span class="k"&gt;in&lt;/span&gt; /var/log/&lt;span class="k"&gt;*&lt;/span&gt;.log&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="k"&gt;continue
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Processing &lt;/span&gt;&lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;

&lt;span class="c"&gt;# C-style for&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0&lt;span class="p"&gt;;&lt;/span&gt; i&amp;lt;5&lt;span class="p"&gt;;&lt;/span&gt; i++&lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"index &lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;

&lt;span class="c"&gt;# while — read line by line (safest pattern)&lt;/span&gt;
&lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nv"&gt;IFS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;read&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; line&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&amp;gt; &lt;/span&gt;&lt;span class="nv"&gt;$line&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt; &amp;lt; /etc/hosts

&lt;span class="c"&gt;# until with retry&lt;/span&gt;
&lt;span class="nv"&gt;attempts&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0
&lt;span class="k"&gt;until &lt;/span&gt;curl &lt;span class="nt"&gt;-sf&lt;/span&gt; https://api.example.com/health &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="o"&gt;((&lt;/span&gt;attempts++&lt;span class="o"&gt;))&lt;/span&gt;
    &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nv"&gt;$attempts&lt;/span&gt; &lt;span class="nt"&gt;-ge&lt;/span&gt; 5 &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"giving up"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="nb"&gt;sleep &lt;/span&gt;2
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Warning:&lt;/strong&gt; &lt;code&gt;for f in $(ls *.log)&lt;/code&gt; is a classic anti-pattern. If filenames have spaces, they get split. Use &lt;code&gt;for f in *.log&lt;/code&gt; (glob directly) instead.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Ten Practical Examples
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Retry with exponential backoff:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;MAX_TRIES&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5
&lt;span class="nv"&gt;DELAY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;try&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1&lt;span class="p"&gt;;&lt;/span&gt; try&amp;lt;&lt;span class="o"&gt;=&lt;/span&gt;MAX_TRIES&lt;span class="p"&gt;;&lt;/span&gt; try++&lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    if &lt;/span&gt;curl &lt;span class="nt"&gt;-sfL&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$URL&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /tmp/out&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Success on attempt &lt;/span&gt;&lt;span class="nv"&gt;$try&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
        &lt;span class="nb"&gt;exit &lt;/span&gt;0
    &lt;span class="k"&gt;fi
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Attempt &lt;/span&gt;&lt;span class="nv"&gt;$try&lt;/span&gt;&lt;span class="s2"&gt; failed, sleeping &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DELAY&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;s..."&lt;/span&gt;
    &lt;span class="nb"&gt;sleep&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DELAY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nv"&gt;DELAY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;DELAY &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt;
&lt;span class="k"&gt;done
&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"All &lt;/span&gt;&lt;span class="nv"&gt;$MAX_TRIES&lt;/span&gt;&lt;span class="s2"&gt; attempts failed"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
&lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Wait for a port to be open (Docker Compose pattern):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;HOST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;db
&lt;span class="nv"&gt;PORT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5432
&lt;span class="k"&gt;until&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/tcp/&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOST&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;/&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PORT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; 2&amp;gt;/dev/null&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Waiting for &lt;/span&gt;&lt;span class="nv"&gt;$HOST&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;$PORT&lt;/span&gt;&lt;span class="s2"&gt;..."&lt;/span&gt;
    &lt;span class="nb"&gt;sleep &lt;/span&gt;1
&lt;span class="k"&gt;done
&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOST&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;$PORT&lt;/span&gt;&lt;span class="s2"&gt; is up"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Read a CSV line-by-line:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nv"&gt;IFS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;, &lt;span class="nb"&gt;read&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; name email role&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$name&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"name"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;continue&lt;/span&gt;   &lt;span class="c"&gt;# skip header&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"User: &lt;/span&gt;&lt;span class="nv"&gt;$name&lt;/span&gt;&lt;span class="s2"&gt; &amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;$email&lt;/span&gt;&lt;span class="s2"&gt;&amp;gt; (&lt;/span&gt;&lt;span class="nv"&gt;$role&lt;/span&gt;&lt;span class="s2"&gt;)"&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt; &amp;lt; users.csv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. Interactive menu:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    &lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;MENU&lt;/span&gt;&lt;span class="sh"&gt;
1) Show uptime
2) Show disk
q) Quit
&lt;/span&gt;&lt;span class="no"&gt;MENU
&lt;/span&gt;    &lt;span class="nb"&gt;read&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"Choice: "&lt;/span&gt; ch
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ch&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in
        &lt;/span&gt;1&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;uptime&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
        2&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;df&lt;/span&gt; &lt;span class="nt"&gt;-h&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
        q|Q&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;break&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
        &lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Invalid choice"&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
    &lt;span class="k"&gt;esac&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Interview tip:&lt;/strong&gt; A common screening question is "write a script to find the 5 largest files in a directory tree." Answer: &lt;code&gt;find /path -type f -printf '%s %p\n' | sort -rn | head -5&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Bash Functions and Advanced
&lt;/h2&gt;

&lt;p&gt;Once your scripts grow past 50 lines, you need functions, arrays, and disciplined error handling. This section covers the constructs that separate "shell script someone hacked together" from "production automation."&lt;/p&gt;

&lt;h3&gt;
  
  
  Function Definitions
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Style 1 — POSIX, works in /bin/sh too&lt;/span&gt;
greet&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Hello, &lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;!"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# Style 2 — bash keyword form&lt;/span&gt;
&lt;span class="k"&gt;function &lt;/span&gt;greet&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Hello, &lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;!"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

greet Pushkar     &lt;span class="c"&gt;# Hello, Pushkar!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Function Arguments and Return Values
&lt;/h3&gt;

&lt;p&gt;Inside a function, &lt;code&gt;$1&lt;/code&gt;, &lt;code&gt;$2&lt;/code&gt;, &lt;code&gt;$@&lt;/code&gt;, &lt;code&gt;$#&lt;/code&gt; refer to the function's arguments — &lt;strong&gt;not&lt;/strong&gt; the script's. Bash &lt;code&gt;return N&lt;/code&gt; only sets the exit status (0-255); to return data, &lt;strong&gt;echo it and capture with &lt;code&gt;$()&lt;/code&gt;&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;get_kernel&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;uname&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;            &lt;span class="c"&gt;# write to stdout&lt;/span&gt;
is_root&lt;span class="o"&gt;()&lt;/span&gt;    &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nv"&gt;$EUID&lt;/span&gt; &lt;span class="nt"&gt;-eq&lt;/span&gt; 0 &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;   &lt;span class="c"&gt;# last command's exit status IS the return&lt;/span&gt;

&lt;span class="nv"&gt;KERNEL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;get_kernel&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;is_root&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Running as root"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Local Variables
&lt;/h3&gt;

&lt;p&gt;By default all bash variables are global. &lt;code&gt;local&lt;/code&gt; (only valid inside functions) keeps a variable scoped:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;counter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0
bump&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;counter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;100    &lt;span class="c"&gt;# shadows the global&lt;/span&gt;
    &lt;span class="nv"&gt;counter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;counter &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"inside: &lt;/span&gt;&lt;span class="nv"&gt;$counter&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
bump                &lt;span class="c"&gt;# inside: 101&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"outside: &lt;/span&gt;&lt;span class="nv"&gt;$counter&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;   &lt;span class="c"&gt;# outside: 0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; &lt;code&gt;local var=$(some_cmd)&lt;/code&gt; &lt;em&gt;masks&lt;/em&gt; the exit code of &lt;code&gt;some_cmd&lt;/code&gt; because &lt;code&gt;local&lt;/code&gt; itself succeeds. Under &lt;code&gt;set -e&lt;/code&gt;, failures silently disappear. Safe idiom: declare first, assign second.&lt;/p&gt;


&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;local &lt;/span&gt;var
&lt;span class="nv"&gt;var&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;some_cmd&lt;span class="si"&gt;)&lt;/span&gt;   &lt;span class="c"&gt;# now set -e catches a failure here&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Arrays
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;servers&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;web01 web02 db01 cache01&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;servers&lt;/span&gt;&lt;span class="p"&gt;[0]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;       &lt;span class="c"&gt;# web01 (zero-indexed)&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;servers&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;       &lt;span class="c"&gt;# all elements as separate words&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${#&lt;/span&gt;&lt;span class="nv"&gt;servers&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;      &lt;span class="c"&gt;# 4 (length)&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="p"&gt;!servers[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;      &lt;span class="c"&gt;# 0 1 2 3 (indices)&lt;/span&gt;
servers+&lt;span class="o"&gt;=(&lt;/span&gt;db02 db03&lt;span class="o"&gt;)&lt;/span&gt;         &lt;span class="c"&gt;# append&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;s &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;servers&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Server: &lt;/span&gt;&lt;span class="nv"&gt;$s&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;done
&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;servers&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;:1:2&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;     &lt;span class="c"&gt;# slice — from index 1, take 2&lt;/span&gt;
&lt;span class="nb"&gt;mapfile&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; &lt;span class="nb"&gt;users&lt;/span&gt; &amp;lt; &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;: &lt;span class="nt"&gt;-f1&lt;/span&gt; /etc/passwd&lt;span class="o"&gt;)&lt;/span&gt;   &lt;span class="c"&gt;# read command output into array&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Associative Arrays (Hash Maps)
&lt;/h3&gt;

&lt;p&gt;Bash 4+ via &lt;code&gt;declare -A&lt;/code&gt;. Ubuntu 24.04 ships bash 5.2:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;declare&lt;/span&gt; &lt;span class="nt"&gt;-A&lt;/span&gt; env_url
env_url[dev]&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://dev.example.com"&lt;/span&gt;
env_url[prod]&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://example.com"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;env_url&lt;/span&gt;&lt;span class="p"&gt;[prod]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;key &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="p"&gt;!env_url[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    &lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'%-10s -&amp;gt; %s\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$key&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;env_url&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;done
if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; env_url[qa] &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"qa defined"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;else &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"qa missing"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Exit Codes and Convention
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Code&lt;/th&gt;
&lt;th&gt;Convention&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;Success&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;General error (catch-all)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Misuse / invalid arguments&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;126&lt;/td&gt;
&lt;td&gt;Command found but not executable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;127&lt;/td&gt;
&lt;td&gt;Command not found&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;128 + N&lt;/td&gt;
&lt;td&gt;Killed by signal N (130 = Ctrl-C, 137 = SIGKILL)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;255&lt;/td&gt;
&lt;td&gt;Exit code out of range&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Follow the exit-code convention. CI systems (Jenkins, GitLab, GitHub Actions) only know "did the job succeed or fail" by exit code. If your script &lt;code&gt;exit 0&lt;/code&gt;s on error, the pipeline shows green while the deploy actually failed. Always non-zero exit on any failure path.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  trap — Cleanup on Exit/Signal
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-euo&lt;/span&gt; pipefail
&lt;span class="nv"&gt;TMPDIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;mktemp&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
cleanup&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;rc&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$?&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Cleaning up (exit=&lt;/span&gt;&lt;span class="nv"&gt;$rc&lt;/span&gt;&lt;span class="s2"&gt;)..."&lt;/span&gt;
    &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$TMPDIR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$rc&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="nb"&gt;trap &lt;/span&gt;cleanup EXIT                          &lt;span class="c"&gt;# EXIT = whenever the script ends&lt;/span&gt;
&lt;span class="nb"&gt;trap&lt;/span&gt; &lt;span class="s1"&gt;'echo "Interrupted"; exit 130'&lt;/span&gt; INT TERM   &lt;span class="c"&gt;# INT = Ctrl-C, TERM = kill&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"working in &lt;/span&gt;&lt;span class="nv"&gt;$TMPDIR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  getopts — Argument Parsing
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nb"&gt;getopts&lt;/span&gt; &lt;span class="s2"&gt;"e:v:dh"&lt;/span&gt; opt&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    case&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$opt&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in
        &lt;/span&gt;e&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;ENV&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$OPTARG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
        v&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$OPTARG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
        d&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;DRY_RUN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="p"&gt;;;&lt;/span&gt;
        h&lt;span class="p"&gt;)&lt;/span&gt; usage &lt;span class="p"&gt;;;&lt;/span&gt;
        &lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; usage &lt;span class="p"&gt;;;&lt;/span&gt;
    &lt;span class="k"&gt;esac&lt;/span&gt;
&lt;span class="k"&gt;done
&lt;/span&gt;&lt;span class="nb"&gt;shift&lt;/span&gt; &lt;span class="k"&gt;$((&lt;/span&gt;OPTIND &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt;
&lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ENV&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$VERSION&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"-e and -v required"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;2&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Heredocs and Here-Strings
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Pushkar
&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
Hello, &lt;/span&gt;&lt;span class="nv"&gt;$NAME&lt;/span&gt;&lt;span class="sh"&gt;
Today is &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%F&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
Literal &lt;/span&gt;&lt;span class="nv"&gt;$NAME&lt;/span&gt;&lt;span class="sh"&gt; and &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="sh"&gt; — nothing expands here
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s1"&gt;'^[A-Z]'&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LINE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;     &lt;span class="c"&gt;# here-string — feed a single string as stdin&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Source-Library Pattern
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# File: lib/common.sh&lt;/span&gt;
log&lt;span class="o"&gt;()&lt;/span&gt;  &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'[%s] %s\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%FT%T&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$*&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
warn&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; log &lt;span class="s2"&gt;"WARN: &lt;/span&gt;&lt;span class="nv"&gt;$*&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
die&lt;span class="o"&gt;()&lt;/span&gt;  &lt;span class="o"&gt;{&lt;/span&gt; log &lt;span class="s2"&gt;"FATAL: &lt;/span&gt;&lt;span class="nv"&gt;$*&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
require_root&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nv"&gt;$EUID&lt;/span&gt; &lt;span class="nt"&gt;-eq&lt;/span&gt; 0 &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; die &lt;span class="s2"&gt;"must run as root"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# File: deploy.sh&lt;/span&gt;
&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-euo&lt;/span&gt; pipefail
&lt;span class="nv"&gt;SCRIPT_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;BASH_SOURCE&lt;/span&gt;&lt;span class="p"&gt;[0]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;source&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SCRIPT_DIR&lt;/span&gt;&lt;span class="s2"&gt;/lib/common.sh"&lt;/span&gt;
require_root
log &lt;span class="s2"&gt;"Starting deploy"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Install &lt;code&gt;shellcheck&lt;/code&gt; (&lt;code&gt;sudo apt install shellcheck&lt;/code&gt;) and run it on every script before committing. It catches ~80% of bash bugs — quoting issues, unset variables, deprecated syntax, common SC2086 word-splitting traps. Hook it into your CI.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Real DevOps Bash Scripts
&lt;/h2&gt;

&lt;p&gt;Below are complete, working scripts you would actually run in production. Each uses strict mode, functions, error handling, and proper logging. They cover the automation patterns you will encounter repeatedly as a DevOps engineer.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Real world:&lt;/strong&gt; Every script here is based on patterns used in production: locking, retries, Slack alerts, S3 upload, rollback on health-check fail. Tweak the paths and service names for your environment.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Script 1: server_health_check.sh
&lt;/h3&gt;

&lt;p&gt;One-shot dashboard of CPU, memory, disk, and top processes. Exits non-zero if any threshold is breached so it can drive alerting from cron.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="c"&gt;# server_health_check.sh — quick host health snapshot&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-euo&lt;/span&gt; pipefail
&lt;span class="nv"&gt;IFS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;$'&lt;/span&gt;&lt;span class="se"&gt;\n\t&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;

&lt;span class="nv"&gt;CPU_WARN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;80
&lt;span class="nv"&gt;MEM_WARN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;85
&lt;span class="nv"&gt;DISK_WARN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;80
&lt;span class="nv"&gt;EXIT_CODE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0

hr&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'%s\n'&lt;/span&gt; &lt;span class="s2"&gt;"------------------------------------------------------------"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;

header&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    hr
    &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'%s @ %s\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;hostname&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +&lt;span class="s1"&gt;'%F %T %Z'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'kernel: %s   uptime:%s\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;uname&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;uptime&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    hr
&lt;span class="o"&gt;}&lt;/span&gt;

cpu_section&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;local &lt;/span&gt;idle1 total1 idle2 total2 usage
    &lt;span class="nb"&gt;read&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; _ a b c idle1 _ &amp;lt; /proc/stat
    &lt;span class="nv"&gt;total1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;a &lt;span class="o"&gt;+&lt;/span&gt; b &lt;span class="o"&gt;+&lt;/span&gt; c &lt;span class="o"&gt;+&lt;/span&gt; idle1&lt;span class="k"&gt;))&lt;/span&gt;
    &lt;span class="nb"&gt;sleep &lt;/span&gt;1
    &lt;span class="nb"&gt;read&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; _ a b c idle2 _ &amp;lt; /proc/stat
    &lt;span class="nv"&gt;total2&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;a &lt;span class="o"&gt;+&lt;/span&gt; b &lt;span class="o"&gt;+&lt;/span&gt; c &lt;span class="o"&gt;+&lt;/span&gt; idle2&lt;span class="k"&gt;))&lt;/span&gt;
    &lt;span class="nv"&gt;usage&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="o"&gt;((&lt;/span&gt;total2 &lt;span class="o"&gt;-&lt;/span&gt; total1&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;idle2 &lt;span class="o"&gt;-&lt;/span&gt; idle1&lt;span class="k"&gt;))&lt;/span&gt; / &lt;span class="o"&gt;(&lt;/span&gt;total2 - total1&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;))&lt;/span&gt;
    &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'CPU usage:  %d%%   load: %s\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$usage&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="s1"&gt;' '&lt;/span&gt; &lt;span class="nt"&gt;-f1-3&lt;/span&gt; /proc/loadavg&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="o"&gt;((&lt;/span&gt; usage &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; CPU_WARN &lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"  WARN: cpu &amp;gt; &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CPU_WARN&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;%"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;EXIT_CODE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

mem_section&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;local &lt;/span&gt;total used pct
    &lt;span class="nb"&gt;read&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; _ total used _ &amp;lt; &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;free &lt;span class="nt"&gt;-m&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'/^Mem:/'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nv"&gt;pct&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; used &lt;span class="o"&gt;/&lt;/span&gt; total &lt;span class="k"&gt;))&lt;/span&gt;
    &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'Memory:     %d / %d MiB  (%d%%)\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$used&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$total&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$pct&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="o"&gt;((&lt;/span&gt; pct &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; MEM_WARN &lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"  WARN: memory &amp;gt; &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;MEM_WARN&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;%"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;EXIT_CODE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

disk_section&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'Disks:\n'&lt;/span&gt;
    &lt;span class="nb"&gt;df&lt;/span&gt; &lt;span class="nt"&gt;-hP&lt;/span&gt; &lt;span class="nt"&gt;-x&lt;/span&gt; tmpfs &lt;span class="nt"&gt;-x&lt;/span&gt; devtmpfs | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'NR&amp;gt;1 { printf "  %-25s %5s used of %-5s on %s\n", $1, $5, $2, $6 }'&lt;/span&gt;
    &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nb"&gt;read&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; usage mount&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="o"&gt;((&lt;/span&gt; usage &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; DISK_WARN &lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"  WARN: &lt;/span&gt;&lt;span class="nv"&gt;$mount&lt;/span&gt;&lt;span class="s2"&gt; &amp;gt; &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DISK_WARN&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;% (&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;usage&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;%)"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;EXIT_CODE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;done&lt;/span&gt; &amp;lt; &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;df&lt;/span&gt; &lt;span class="nt"&gt;-P&lt;/span&gt; &lt;span class="nt"&gt;-x&lt;/span&gt; tmpfs &lt;span class="nt"&gt;-x&lt;/span&gt; devtmpfs | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'NR&amp;gt;1 { gsub("%","",$5); print $5, $6 }'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

main&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    header
    cpu_section
    mem_section
    disk_section
    hr
    &lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$EXIT_CODE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

main &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Script 2: deploy_app.sh
&lt;/h3&gt;

&lt;p&gt;Git pull, npm build, systemd restart, health check, and automatic rollback on failure. Uses a lockfile to prevent concurrent deploys.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="c"&gt;# deploy_app.sh — pull, build, restart, verify, rollback on fail&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-euo&lt;/span&gt; pipefail

&lt;span class="nv"&gt;APP_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/opt/myapp"&lt;/span&gt;
&lt;span class="nv"&gt;SERVICE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"myapp"&lt;/span&gt;
&lt;span class="nv"&gt;HEALTH_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"http://127.0.0.1:3000/health"&lt;/span&gt;
&lt;span class="nv"&gt;LOCK&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/var/run/deploy_&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SERVICE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.lock"&lt;/span&gt;
&lt;span class="nv"&gt;HEALTH_TRIES&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;20

log&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'[%s] %s\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%FT%T&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$*&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
die&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; log &lt;span class="s2"&gt;"FATAL: &lt;/span&gt;&lt;span class="nv"&gt;$*&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# Single-instance lock&lt;/span&gt;
&lt;span class="nb"&gt;exec &lt;/span&gt;9&amp;gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LOCK&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
flock &lt;span class="nt"&gt;-n&lt;/span&gt; 9 &lt;span class="o"&gt;||&lt;/span&gt; die &lt;span class="s2"&gt;"another deploy is in progress"&lt;/span&gt;

&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$APP_DIR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; die &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$APP_DIR&lt;/span&gt;&lt;span class="s2"&gt; not found"&lt;/span&gt;
&lt;span class="nv"&gt;OLD_SHA&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git rev-parse HEAD&lt;span class="si"&gt;)&lt;/span&gt;
log &lt;span class="s2"&gt;"current SHA: &lt;/span&gt;&lt;span class="nv"&gt;$OLD_SHA&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

git fetch &lt;span class="nt"&gt;--quiet&lt;/span&gt; origin
git reset &lt;span class="nt"&gt;--hard&lt;/span&gt; origin/main
&lt;span class="nv"&gt;NEW_SHA&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git rev-parse HEAD&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$OLD_SHA&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$NEW_SHA&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; log &lt;span class="s2"&gt;"already at latest, no-op"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;0&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
log &lt;span class="s2"&gt;"new SHA: &lt;/span&gt;&lt;span class="nv"&gt;$NEW_SHA&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

rollback&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    log &lt;span class="s2"&gt;"ROLLING BACK to &lt;/span&gt;&lt;span class="nv"&gt;$OLD_SHA&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    git reset &lt;span class="nt"&gt;--hard&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$OLD_SHA&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    npm ci &lt;span class="nt"&gt;--omit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;dev &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm run build &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true
    &lt;/span&gt;systemctl restart &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SERVICE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    die &lt;span class="s2"&gt;"deploy failed, rolled back"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

npm ci &lt;span class="nt"&gt;--omit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;dev &lt;span class="o"&gt;||&lt;/span&gt; rollback
npm run build &lt;span class="o"&gt;||&lt;/span&gt; rollback
systemctl restart &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SERVICE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; rollback

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1&lt;span class="p"&gt;;&lt;/span&gt; i&amp;lt;&lt;span class="o"&gt;=&lt;/span&gt;HEALTH_TRIES&lt;span class="p"&gt;;&lt;/span&gt; i++&lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    if &lt;/span&gt;curl &lt;span class="nt"&gt;-fsS&lt;/span&gt; &lt;span class="nt"&gt;-m&lt;/span&gt; 3 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HEALTH_URL&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;log &lt;span class="s2"&gt;"healthy after &lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="s2"&gt; probe(s)"&lt;/span&gt;
        log &lt;span class="s2"&gt;"deploy OK: &lt;/span&gt;&lt;span class="nv"&gt;$OLD_SHA&lt;/span&gt;&lt;span class="s2"&gt; -&amp;gt; &lt;/span&gt;&lt;span class="nv"&gt;$NEW_SHA&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
        &lt;span class="nb"&gt;exit &lt;/span&gt;0
    &lt;span class="k"&gt;fi
    &lt;/span&gt;&lt;span class="nb"&gt;sleep &lt;/span&gt;2
&lt;span class="k"&gt;done

&lt;/span&gt;rollback
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Script 3: service_monitor.sh
&lt;/h3&gt;

&lt;p&gt;Watchdog for systemd units. Restarts down services and posts to Slack on incident. Designed for a cron-every-minute or systemd timer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="c"&gt;# service_monitor.sh — auto-restart services and Slack on failure&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-euo&lt;/span&gt; pipefail

&lt;span class="nv"&gt;SERVICES&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;nginx postgresql redis-server docker&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;MAX_RESTART&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3
&lt;span class="nv"&gt;SLACK_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SLACK_WEBHOOK_URL&lt;/span&gt;&lt;span class="k"&gt;:-}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;HOST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;hostname&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

log&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'[%s] %s\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%FT%T&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$*&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;

slack&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SLACK_URL&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; log &lt;span class="s2"&gt;"SLACK_WEBHOOK_URL not set, skipping notify"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    curl &lt;span class="nt"&gt;-fsS&lt;/span&gt; &lt;span class="nt"&gt;-m&lt;/span&gt; 5 &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;--data&lt;/span&gt; &lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:rotating_light: [&lt;/span&gt;&lt;span class="nv"&gt;$HOST&lt;/span&gt;&lt;span class="s2"&gt;] &lt;/span&gt;&lt;span class="nv"&gt;$msg&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;}"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SLACK_URL&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null &lt;span class="o"&gt;||&lt;/span&gt; log &lt;span class="s2"&gt;"WARN: slack post failed"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

check_service&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;svc&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;systemctl is-active &lt;span class="nt"&gt;--quiet&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$svc&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;log &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$svc&lt;/span&gt;&lt;span class="s2"&gt;: OK"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;return &lt;/span&gt;0
    &lt;span class="k"&gt;fi
    &lt;/span&gt;log &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$svc&lt;/span&gt;&lt;span class="s2"&gt;: DOWN — attempting restart"&lt;/span&gt;
    &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;attempt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="o"&gt;((&lt;/span&gt;attempt &amp;lt;&lt;span class="o"&gt;=&lt;/span&gt; MAX_RESTART&lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
        &lt;/span&gt;systemctl restart &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$svc&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true
        sleep&lt;/span&gt; &lt;span class="k"&gt;$((&lt;/span&gt;attempt &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;systemctl is-active &lt;span class="nt"&gt;--quiet&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$svc&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
            &lt;/span&gt;log &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$svc&lt;/span&gt;&lt;span class="s2"&gt;: recovered on attempt &lt;/span&gt;&lt;span class="nv"&gt;$attempt&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
            slack &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$svc&lt;/span&gt;&lt;span class="s2"&gt; was DOWN, restarted successfully on attempt &lt;/span&gt;&lt;span class="nv"&gt;$attempt&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
            &lt;span class="k"&gt;return &lt;/span&gt;0
        &lt;span class="k"&gt;fi&lt;/span&gt;
        &lt;span class="o"&gt;((&lt;/span&gt;attempt++&lt;span class="o"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;done
    &lt;/span&gt;log &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$svc&lt;/span&gt;&lt;span class="s2"&gt;: FAILED after &lt;/span&gt;&lt;span class="nv"&gt;$MAX_RESTART&lt;/span&gt;&lt;span class="s2"&gt; attempts"&lt;/span&gt;
    slack &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$svc&lt;/span&gt;&lt;span class="s2"&gt; is DOWN and failed to restart after &lt;/span&gt;&lt;span class="nv"&gt;$MAX_RESTART&lt;/span&gt;&lt;span class="s2"&gt; attempts"&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;1
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;failures&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0
&lt;span class="k"&gt;for &lt;/span&gt;s &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SERVICES&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    &lt;/span&gt;check_service &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$s&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;((&lt;/span&gt;failures++&lt;span class="o"&gt;))&lt;/span&gt;
&lt;span class="k"&gt;done
&lt;/span&gt;&lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$failures&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Script 4: log_rotate.sh
&lt;/h3&gt;

&lt;p&gt;Minimal logrotate alternative for ad-hoc log directories. Gzips logs older than N days, deletes anything older than M days. Safe for cron.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="c"&gt;# log_rotate.sh — compress old logs, delete very old ones&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-euo&lt;/span&gt; pipefail

&lt;span class="nv"&gt;LOG_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;1&lt;/span&gt;:?Usage:&lt;span class="p"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$0&lt;/span&gt;&lt;span class="p"&gt; &amp;lt;dir&amp;gt; [compress_days] [delete_days]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;COMPRESS_DAYS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;2&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;7&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;DELETE_DAYS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;3&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;30&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

log&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'[%s] %s\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%FT%T&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$*&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LOG_DIR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; log &lt;span class="s2"&gt;"FATAL: &lt;/span&gt;&lt;span class="nv"&gt;$LOG_DIR&lt;/span&gt;&lt;span class="s2"&gt; not found"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
log &lt;span class="s2"&gt;"Rotating in &lt;/span&gt;&lt;span class="nv"&gt;$LOG_DIR&lt;/span&gt;&lt;span class="s2"&gt; (gzip &amp;gt;&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;COMPRESS_DAYS&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;d, delete &amp;gt;&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DELETE_DAYS&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;d)"&lt;/span&gt;

&lt;span class="nv"&gt;compressed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0
&lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nv"&gt;IFS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;read&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt; f&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    &lt;/span&gt;&lt;span class="nb"&gt;gzip&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="o"&gt;((&lt;/span&gt;compressed++&lt;span class="o"&gt;))&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt; &amp;lt; &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;find &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LOG_DIR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s1"&gt;'*.log'&lt;/span&gt; &lt;span class="nt"&gt;-mtime&lt;/span&gt; +&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$COMPRESS_DAYS&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-print0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
log &lt;span class="s2"&gt;"Compressed &lt;/span&gt;&lt;span class="nv"&gt;$compressed&lt;/span&gt;&lt;span class="s2"&gt; file(s)"&lt;/span&gt;

&lt;span class="nv"&gt;deleted&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;find &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LOG_DIR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="se"&gt;\(&lt;/span&gt; &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s1"&gt;'*.log'&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s1"&gt;'*.log.gz'&lt;/span&gt; &lt;span class="se"&gt;\)&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
            &lt;span class="nt"&gt;-mtime&lt;/span&gt; +&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DELETE_DAYS&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-print&lt;/span&gt; &lt;span class="nt"&gt;-delete&lt;/span&gt; | &lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
log &lt;span class="s2"&gt;"Deleted &lt;/span&gt;&lt;span class="nv"&gt;$deleted&lt;/span&gt;&lt;span class="s2"&gt; file(s)"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Script 5: log_analysis.sh
&lt;/h3&gt;

&lt;p&gt;Parse nginx access logs for top IPs, top URLs, and 4xx/5xx counts. Drop-in incident-response tool.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="c"&gt;# log_analysis.sh — top IPs, URLs, status code breakdown from nginx logs&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-euo&lt;/span&gt; pipefail

&lt;span class="nv"&gt;LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;1&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="p"&gt;/var/log/nginx/access.log&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;TOP_N&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;2&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;10&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LOG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"cannot read &lt;/span&gt;&lt;span class="nv"&gt;$LOG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LOG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;.gz &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then &lt;/span&gt;&lt;span class="nv"&gt;READ&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"zcat"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;else &lt;/span&gt;&lt;span class="nv"&gt;READ&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"cat"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"=== Summary of &lt;/span&gt;&lt;span class="nv"&gt;$LOG&lt;/span&gt;&lt;span class="s2"&gt; ==="&lt;/span&gt;
&lt;span class="nv"&gt;TOTAL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nv"&gt;$READ&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LOG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Total requests: &lt;/span&gt;&lt;span class="nv"&gt;$TOTAL&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;echo

echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Top &lt;/span&gt;&lt;span class="nv"&gt;$TOP_N&lt;/span&gt;&lt;span class="s2"&gt; IPs ---"&lt;/span&gt;
&lt;span class="nv"&gt;$READ&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LOG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{ print $1 }'&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; | &lt;span class="nb"&gt;uniq&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-rn&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$TOP_N&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="nb"&gt;echo
echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Top &lt;/span&gt;&lt;span class="nv"&gt;$TOP_N&lt;/span&gt;&lt;span class="s2"&gt; URLs ---"&lt;/span&gt;
&lt;span class="nv"&gt;$READ&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LOG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{ print $7 }'&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; | &lt;span class="nb"&gt;uniq&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-rn&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$TOP_N&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="nb"&gt;echo
echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Status code breakdown ---"&lt;/span&gt;
&lt;span class="nv"&gt;$READ&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LOG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{
    code=$9
    counts[code]++
    if (code+0 &amp;gt;= 400 &amp;amp;&amp;amp; code+0 &amp;lt; 500) c4xx++
    else if (code+0 &amp;gt;= 500) c5xx++
}
END {
    for (c in counts) printf "%5d %s\n", counts[c], c | "sort -rn"
    close("sort -rn")
    printf "\nTotal 4xx: %d\nTotal 5xx: %d\n", c4xx+0, c5xx+0
}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Key Patterns to Internalize
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Functions and libraries:&lt;/strong&gt; Split helpers into &lt;code&gt;lib/common.sh&lt;/code&gt; and source it. Define &lt;code&gt;log()&lt;/code&gt;, &lt;code&gt;warn()&lt;/code&gt;, and &lt;code&gt;die()&lt;/code&gt; functions in every serious script.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;trap for cleanup:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;TMPDIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;mktemp&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
cleanup&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$TMPDIR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="nb"&gt;trap &lt;/span&gt;cleanup EXIT
&lt;span class="nb"&gt;trap&lt;/span&gt; &lt;span class="s1"&gt;'echo "Interrupted"; exit 130'&lt;/span&gt; INT TERM
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;getopts for argument parsing:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nb"&gt;getopts&lt;/span&gt; &lt;span class="s2"&gt;"e:v:dh"&lt;/span&gt; opt&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    case&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$opt&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in
        &lt;/span&gt;e&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;ENV&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$OPTARG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
        v&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$OPTARG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
        d&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;DRY_RUN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="p"&gt;;;&lt;/span&gt;
        h&lt;span class="p"&gt;)&lt;/span&gt; usage &lt;span class="p"&gt;;;&lt;/span&gt;
        &lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; usage &lt;span class="p"&gt;;;&lt;/span&gt;
    &lt;span class="k"&gt;esac&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Interview tip:&lt;/strong&gt; When asked "design a deploy script," walk through: (1) idempotency, (2) lockfile to prevent concurrent runs, (3) capture old state for rollback, (4) health check after restart, (5) automatic rollback path, (6) logging and notifications. The &lt;code&gt;deploy_app.sh&lt;/code&gt; above hits all six — internalize the pattern.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Warning:&lt;/strong&gt; Never write &lt;code&gt;rm -rf "$VAR/"&lt;/code&gt; without &lt;code&gt;set -u&lt;/code&gt; and an explicit non-empty check. Empty variable + slash = root-level delete. Always: &lt;code&gt;[[ -n "${DIR:-}" &amp;amp;&amp;amp; -d "$DIR" ]] || die "bad DIR"&lt;/code&gt; before any destructive operation.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Linux Logs
&lt;/h2&gt;

&lt;p&gt;For a DevOps engineer, logs are the &lt;strong&gt;single source of truth&lt;/strong&gt;. When a production app misbehaves at 3 AM, you don't guess — you grep. On a Linux server you'll be living inside &lt;code&gt;/var/log&lt;/code&gt; and &lt;code&gt;journalctl&lt;/code&gt;. Master these and you can debug anything.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Mental model — Traditional Linux services write plain-text files to &lt;code&gt;/var/log&lt;/code&gt;. Modern systemd services write to the binary &lt;strong&gt;journal&lt;/strong&gt;, queried with &lt;code&gt;journalctl&lt;/code&gt;. On Ubuntu 24.04 you have both.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Key Log Files — Cheat Sheet
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Path&lt;/th&gt;
&lt;th&gt;Distro&lt;/th&gt;
&lt;th&gt;What's in it&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/var/log/syslog&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Debian/Ubuntu&lt;/td&gt;
&lt;td&gt;General system events — almost everything ends up here&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/var/log/messages&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;RHEL/Rocky&lt;/td&gt;
&lt;td&gt;RHEL equivalent of syslog&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/var/log/auth.log&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Debian/Ubuntu&lt;/td&gt;
&lt;td&gt;SSH logins, sudo, PAM, su — every auth event&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/var/log/secure&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;RHEL&lt;/td&gt;
&lt;td&gt;RHEL equivalent of auth.log&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/var/log/kern.log&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Both&lt;/td&gt;
&lt;td&gt;Kernel ring buffer messages (drivers, OOM kills)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/var/log/apt/history.log&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Debian/Ubuntu&lt;/td&gt;
&lt;td&gt;Every apt install/remove/upgrade&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/var/log/dpkg.log&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Debian/Ubuntu&lt;/td&gt;
&lt;td&gt;Low-level package install/remove&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/var/log/nginx/access.log&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;All&lt;/td&gt;
&lt;td&gt;Nginx HTTP requests&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/var/log/nginx/error.log&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;All&lt;/td&gt;
&lt;td&gt;Nginx errors, upstream failures&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/var/log/postgresql/postgresql-*.log&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Ubuntu&lt;/td&gt;
&lt;td&gt;PostgreSQL queries, errors, checkpoints&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/var/log/cloud-init.log&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Cloud VMs&lt;/td&gt;
&lt;td&gt;EC2/GCE first-boot user-data output&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  journalctl — The systemd Journal
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;journalctl &lt;span class="nt"&gt;-e&lt;/span&gt;                       &lt;span class="c"&gt;# jump to end (most recent)&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;journalctl &lt;span class="nt"&gt;-r&lt;/span&gt;                       &lt;span class="c"&gt;# reverse — newest first&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;journalctl &lt;span class="nt"&gt;-n&lt;/span&gt; 50                    &lt;span class="c"&gt;# last 50 lines&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;journalctl &lt;span class="nt"&gt;-f&lt;/span&gt;                       &lt;span class="c"&gt;# follow (like tail -f)&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;journalctl &lt;span class="nt"&gt;-u&lt;/span&gt; nginx                 &lt;span class="c"&gt;# only nginx.service&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;journalctl &lt;span class="nt"&gt;-u&lt;/span&gt; nginx &lt;span class="nt"&gt;-f&lt;/span&gt;              &lt;span class="c"&gt;# follow nginx live&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;journalctl &lt;span class="nt"&gt;--since&lt;/span&gt; &lt;span class="s2"&gt;"1 hour ago"&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;journalctl &lt;span class="nt"&gt;--since&lt;/span&gt; today
&lt;span class="nv"&gt;$ &lt;/span&gt;journalctl &lt;span class="nt"&gt;-p&lt;/span&gt; err                   &lt;span class="c"&gt;# priority err or worse&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;journalctl &lt;span class="nt"&gt;-b&lt;/span&gt;                       &lt;span class="c"&gt;# this boot only&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;journalctl &lt;span class="nt"&gt;-b&lt;/span&gt; &lt;span class="nt"&gt;-1&lt;/span&gt;                    &lt;span class="c"&gt;# previous boot&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;journalctl &lt;span class="nt"&gt;-k&lt;/span&gt;                       &lt;span class="c"&gt;# kernel only (dmesg-style)&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;journalctl &lt;span class="nt"&gt;--disk-usage&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;journalctl &lt;span class="nt"&gt;--vacuum-time&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;7d     &lt;span class="c"&gt;# keep only last 7 days&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;journalctl &lt;span class="nt"&gt;--vacuum-size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;500M   &lt;span class="c"&gt;# cap at 500 MB&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Priority levels (numeric and named both work with &lt;code&gt;-p&lt;/code&gt;): 0 emerg, 1 alert, 2 crit, 3 err, 4 warning, 5 notice, 6 info, 7 debug.&lt;/p&gt;

&lt;h3&gt;
  
  
  logrotate — Stops Disks From Filling Up
&lt;/h3&gt;

&lt;p&gt;Logs grow forever unless rotated. &lt;code&gt;logrotate&lt;/code&gt; runs daily via &lt;code&gt;/etc/cron.daily/logrotate&lt;/code&gt; and reads &lt;code&gt;/etc/logrotate.conf&lt;/code&gt; plus per-service files in &lt;code&gt;/etc/logrotate.d/&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# /etc/logrotate.d/myapp&lt;/span&gt;
/var/log/myapp/&lt;span class="k"&gt;*&lt;/span&gt;.log &lt;span class="o"&gt;{&lt;/span&gt;
    daily
    rotate 14              &lt;span class="c"&gt;# keep 14 old copies&lt;/span&gt;
    size 100M              &lt;span class="c"&gt;# OR rotate when file &amp;gt; 100MB&lt;/span&gt;
    compress
    delaycompress
    missingok
    notifempty
    create 0640 myapp adm
    sharedscripts
    postrotate
        systemctl reload myapp.service &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null 2&amp;gt;&amp;amp;1 &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true
    &lt;/span&gt;endscript
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;logrotate &lt;span class="nt"&gt;-d&lt;/span&gt; /etc/logrotate.d/myapp      &lt;span class="c"&gt;# dry run&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;logrotate &lt;span class="nt"&gt;-f&lt;/span&gt; /etc/logrotate.d/myapp      &lt;span class="c"&gt;# force run now&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Without &lt;code&gt;copytruncate&lt;/code&gt; or a &lt;code&gt;postrotate&lt;/code&gt; reload, long-running daemons keep writing to the old (renamed) file descriptor — and you wonder why rotation did nothing. Always reload the service or use &lt;code&gt;copytruncate&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  rsyslog and Centralized Logging
&lt;/h3&gt;

&lt;p&gt;rsyslog reads kernel and app messages and writes them to &lt;code&gt;/var/log/*&lt;/code&gt; (or ships them to a remote server). Syntax: &lt;code&gt;facility.priority    destination&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;auth,authpriv.&lt;span class="k"&gt;*&lt;/span&gt;                 /var/log/auth.log
&lt;span class="k"&gt;*&lt;/span&gt;.&lt;span class="k"&gt;*&lt;/span&gt;  @@logserver.internal:514    &lt;span class="c"&gt;# TCP to a central log server&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;logger &lt;span class="nt"&gt;-p&lt;/span&gt; local0.notice &lt;span class="s2"&gt;"Deploy v2.4.1 started by &lt;/span&gt;&lt;span class="nv"&gt;$USER&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;   &lt;span class="c"&gt;# write to syslog&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One server: grep is fine. Twenty servers: you need an aggregator.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Stack&lt;/th&gt;
&lt;th&gt;Components&lt;/th&gt;
&lt;th&gt;Best for&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ELK / Elastic&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Filebeat → Logstash → Elasticsearch → Kibana&lt;/td&gt;
&lt;td&gt;Full-text search, rich dashboards&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Grafana Loki&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Promtail → Loki → Grafana&lt;/td&gt;
&lt;td&gt;Cheap, label-based&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AWS CloudWatch Logs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;CW agent → CW Logs → Logs Insights&lt;/td&gt;
&lt;td&gt;AWS-native, zero infra&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Real DevOps Log Examples
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'$9 ~ /^5/ {print $9}'&lt;/span&gt; /var/log/nginx/access.log | &lt;span class="nb"&gt;sort&lt;/span&gt; | &lt;span class="nb"&gt;uniq&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt;   &lt;span class="c"&gt;# 5xx breakdown&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $1}'&lt;/span&gt; /var/log/nginx/access.log | &lt;span class="nb"&gt;sort&lt;/span&gt; | &lt;span class="nb"&gt;uniq&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-rn&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt;   &lt;span class="c"&gt;# top IPs&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dmesg &lt;span class="nt"&gt;-T&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"killed process"&lt;/span&gt;          &lt;span class="c"&gt;# was the system OOM-killed?&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;systemctl &lt;span class="nt"&gt;--failed&lt;/span&gt;                                 &lt;span class="c"&gt;# failed services&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo du&lt;/span&gt; &lt;span class="nt"&gt;-sh&lt;/span&gt; /var/log/&lt;span class="k"&gt;*&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-h&lt;/span&gt; | &lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-20&lt;/span&gt;        &lt;span class="c"&gt;# disk usage per log dir&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Real world:&lt;/strong&gt; Tracing an SSH brute-force on a fresh VPS — &lt;code&gt;auth.log&lt;/code&gt; balloons. Count failures and group by attacker IP:&lt;/p&gt;


&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo grep&lt;/span&gt; &lt;span class="s2"&gt;"Failed password"&lt;/span&gt; /var/log/auth.log | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $(NF-3)}'&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; | &lt;span class="nb"&gt;uniq&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-rn&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo grep&lt;/span&gt; &lt;span class="s2"&gt;"Invalid user"&lt;/span&gt; /var/log/auth.log | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $8}'&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; | &lt;span class="nb"&gt;uniq&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-rn&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Mitigation: install &lt;code&gt;fail2ban&lt;/code&gt;, set &lt;code&gt;PasswordAuthentication no&lt;/code&gt; in sshd_config, move SSH off port 22, or restrict via &lt;code&gt;ufw allow from &amp;lt;your-IP&amp;gt; to any port 22&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  System Monitoring
&lt;/h2&gt;

&lt;p&gt;Monitoring is the second pillar after logs. When a customer says "the site is slow," you need to &lt;strong&gt;know within 30 seconds&lt;/strong&gt; whether it's CPU, memory, disk I/O, or network.&lt;/p&gt;

&lt;h3&gt;
  
  
  uptime — Load Averages
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;uptime
 &lt;/span&gt;11:42:18 up 14 days,  3:22,  2 &lt;span class="nb"&gt;users&lt;/span&gt;,  load average: 1.42, 0.87, 0.63
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;nproc
&lt;/span&gt;4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The three numbers are the average run-queue length over 1, 5, and 15 minutes. Load 1.42 on a 4-core box ≈ 35% utilized. Load 8.0 on 4 cores = processes queueing. 1-min &amp;gt; 15-min means load is increasing.&lt;/p&gt;

&lt;h3&gt;
  
  
  free — Memory
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;free &lt;span class="nt"&gt;-h&lt;/span&gt;
               total        used        free      shared  buff/cache   available
Mem:           7.7Gi       2.1Gi       312Mi        85Mi       5.3Gi       5.3Gi
Swap:          2.0Gi          0B       2.0Gi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; Trap for beginners: "free" looks scary low — that's not the number you want. Linux aggressively uses RAM for &lt;code&gt;buff/cache&lt;/code&gt; (disk caches), reclaimable instantly. The number that matters is &lt;strong&gt;&lt;code&gt;available&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  vmstat and iostat
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;vmstat 2 5     &lt;span class="c"&gt;# sample every 2s, 5 times&lt;/span&gt;
&lt;span class="c"&gt;# watch: r (runnable, should be &amp;lt;= CPU count), si/so (swap, should be 0), wa (CPU% waiting on I/O)&lt;/span&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; sysstat
&lt;span class="nv"&gt;$ &lt;/span&gt;iostat &lt;span class="nt"&gt;-xz&lt;/span&gt; 2   &lt;span class="c"&gt;# extended, skip idle devices&lt;/span&gt;
&lt;span class="c"&gt;# %util near 100% = saturated. await &amp;gt; 20ms on SSD = problem.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  sar — Historical Stats
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;sar&lt;/code&gt; (from &lt;code&gt;sysstat&lt;/code&gt;) records metrics every 10 minutes, so you can look at &lt;em&gt;yesterday&lt;/em&gt;'s CPU.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;sar &lt;span class="nt"&gt;-u&lt;/span&gt; 1 5          &lt;span class="c"&gt;# live CPU&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;sar &lt;span class="nt"&gt;-r&lt;/span&gt; 1 5          &lt;span class="c"&gt;# live memory&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;sar &lt;span class="nt"&gt;-n&lt;/span&gt; DEV 1 5      &lt;span class="c"&gt;# live network per interface&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;sar &lt;span class="nt"&gt;-u&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; /var/log/sysstat/sa25   &lt;span class="c"&gt;# CPU on the 25th&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  dmesg and Hardware Inventory
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dmesg &lt;span class="nt"&gt;-T&lt;/span&gt;                     &lt;span class="c"&gt;# human-readable timestamps&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dmesg &lt;span class="nt"&gt;-T&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; oom       &lt;span class="c"&gt;# OOM killer events&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;lscpu                              &lt;span class="c"&gt;# CPU model, cores, flags&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;lsblk                              &lt;span class="c"&gt;# block devices &amp;amp; mounts&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;lspci &lt;span class="nt"&gt;-nnk&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-A2&lt;/span&gt; Ethernet    &lt;span class="c"&gt;# NIC + driver in use&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  top, htop, and Friends
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;top         &lt;span class="c"&gt;# press 1=per-core, M=sort by memory, P=sort by CPU, c=full command, k=kill&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; htop glances nload iftop iotop ncdu
&lt;span class="nv"&gt;$ &lt;/span&gt;htop        &lt;span class="c"&gt;# color, scrollable, tree view (F5)&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;glances     &lt;span class="c"&gt;# CPU+MEM+NET+DISK+containers in one screen&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;iotop &lt;span class="nt"&gt;-o&lt;/span&gt;    &lt;span class="c"&gt;# disk I/O per process; -o = only active&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;ncdu /var        &lt;span class="c"&gt;# interactive du — navigate and delete junk&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In &lt;code&gt;top&lt;/code&gt;: &lt;code&gt;RES&lt;/code&gt; (resident set size — actual physical RAM) is the number that matters; &lt;code&gt;VIRT&lt;/code&gt; is mostly meaningless.&lt;/p&gt;

&lt;h3&gt;
  
  
  Real Monitoring Scenarios
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;watch &lt;span class="nt"&gt;-n&lt;/span&gt; 5 &lt;span class="s1"&gt;'ps -o pid,rss,vsz,cmd -p $(pgrep -f myapp)'&lt;/span&gt;   &lt;span class="c"&gt;# watch for a memory leak&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;ps aux &lt;span class="nt"&gt;--sort&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;-%mem | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-11&lt;/span&gt;                            &lt;span class="c"&gt;# top 10 by memory&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;ps aux &lt;span class="nt"&gt;--sort&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;-%cpu | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-11&lt;/span&gt;                            &lt;span class="c"&gt;# top 10 by CPU&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;iostat &lt;span class="nt"&gt;-xz&lt;/span&gt; 2 5                                            &lt;span class="c"&gt;# find I/O bottleneck&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;ss &lt;span class="nt"&gt;-tn&lt;/span&gt; state established | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'NR&amp;gt;1{print $5}'&lt;/span&gt; | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;: &lt;span class="nt"&gt;-f1&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; | &lt;span class="nb"&gt;uniq&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-rn&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;ps aux | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'$8 ~ /Z/ {print}'&lt;/span&gt;                           &lt;span class="c"&gt;# find zombie processes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Interview answer for "server slow, what do you do?" — in this exact order: &lt;code&gt;uptime&lt;/code&gt; (load), &lt;code&gt;top&lt;/code&gt;/&lt;code&gt;htop&lt;/code&gt; (process), &lt;code&gt;free -h&lt;/code&gt; (memory), &lt;code&gt;iostat -xz 2&lt;/code&gt; (disk), &lt;code&gt;ss -s&lt;/code&gt; (network), &lt;code&gt;dmesg -T | tail&lt;/code&gt; (kernel). This is Brendan Gregg's USE Method: Utilization, Saturation, Errors.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Cron Jobs
&lt;/h2&gt;

&lt;p&gt;Cron is the Unix scheduler — a daemon that wakes up every minute and runs jobs whose time has come. You'll use it for backups, certificate renewals, log shipping, health checks, and cleanup scripts. It's old (1975!) but still everywhere.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;systemctl status cron           &lt;span class="c"&gt;# Ubuntu/Debian&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;systemctl status crond          &lt;span class="c"&gt;# RHEL&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Crontab Syntax
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# ┌───────────── minute        (0 - 59)
# │ ┌─────────── hour          (0 - 23)
# │ │ ┌───────── day of month  (1 - 31)
# │ │ │ ┌─────── month         (1 - 12)
# │ │ │ │ ┌───── day of week   (0 - 7, where 0 and 7 both = Sunday)
# │ │ │ │ │
# * * * * *  command-to-run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Symbol&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Any value&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;* * * * *&lt;/code&gt; — every minute&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;*/N&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Every N units&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;*/15 * * * *&lt;/code&gt; — every 15 minutes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;N-M&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Range&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;0 9-17 * * *&lt;/code&gt; — every hour, 9 AM to 5 PM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;N,M&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;0 8,12,18 * * *&lt;/code&gt; — 8 AM, noon, 6 PM&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Named shortcuts replace the whole 5-field expression: &lt;code&gt;@reboot&lt;/code&gt;, &lt;code&gt;@hourly&lt;/code&gt;, &lt;code&gt;@daily&lt;/code&gt;, &lt;code&gt;@weekly&lt;/code&gt;, &lt;code&gt;@monthly&lt;/code&gt;, &lt;code&gt;@yearly&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Cron expressions are easy to get subtly wrong. Validate them against a human-readable description and the next few run times with the &lt;a href="https://opscanopy.com/cron-expression-tester/" rel="noopener noreferrer"&gt;Cron Expression Tester&lt;/a&gt; before committing a crontab.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Managing Crontabs
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;crontab &lt;span class="nt"&gt;-e&lt;/span&gt;                &lt;span class="c"&gt;# edit YOUR crontab&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;crontab &lt;span class="nt"&gt;-l&lt;/span&gt;                &lt;span class="c"&gt;# list YOUR crontab&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;crontab &lt;span class="nt"&gt;-ri&lt;/span&gt;               &lt;span class="c"&gt;# delete with confirmation&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;crontab &lt;span class="nt"&gt;-u&lt;/span&gt; deploy &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="c"&gt;# edit deploy user's crontab&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;User crontabs live in &lt;code&gt;/var/spool/cron/crontabs/&amp;lt;user&amp;gt;&lt;/code&gt; — always use &lt;code&gt;crontab -e&lt;/code&gt;, never edit directly.&lt;/p&gt;

&lt;h3&gt;
  
  
  System Cron
&lt;/h3&gt;

&lt;p&gt;System crontab files have an &lt;strong&gt;extra "user" field&lt;/strong&gt; between the schedule and the command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo cat&lt;/span&gt; /etc/crontab
&lt;span class="nv"&gt;SHELL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/bin/sh
&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
&lt;span class="nv"&gt;MAILTO&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;root
&lt;span class="c"&gt;# m h dom mon dow user  command&lt;/span&gt;
17 &lt;span class="k"&gt;*&lt;/span&gt;    &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;   root    &lt;span class="nb"&gt;cd&lt;/span&gt; / &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; run-parts &lt;span class="nt"&gt;--report&lt;/span&gt; /etc/cron.hourly
25 6    &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;   root    &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;-x&lt;/span&gt; /usr/sbin/anacron &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; / &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; run-parts &lt;span class="nt"&gt;--report&lt;/span&gt; /etc/cron.daily&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Path&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/etc/crontab&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Main system schedule (with user field)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/etc/cron.d/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Drop-in files (packages install here)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;/etc/cron.hourly/&lt;/code&gt; &lt;code&gt;.daily/&lt;/code&gt; &lt;code&gt;.weekly/&lt;/code&gt; &lt;code&gt;.monthly/&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Drop a script in, runs on schedule via &lt;code&gt;run-parts&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;run-parts&lt;/code&gt; executes every executable file in a directory. Two gotchas: scripts must be executable (&lt;code&gt;chmod +x&lt;/code&gt;), and filenames must not contain dots.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cron Logs
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo grep &lt;/span&gt;CRON /var/log/syslog | &lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-20&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;journalctl &lt;span class="nt"&gt;-u&lt;/span&gt; cron &lt;span class="nt"&gt;--since&lt;/span&gt; today
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Common DevOps Use Cases
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;SHELL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/bin/bash
&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
&lt;span class="nv"&gt;MAILTO&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;devops@example.com

&lt;span class="c"&gt;# Daily Postgres backup at 02:00&lt;/span&gt;
0 2 &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; /usr/local/bin/backup-postgres.sh &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /var/log/backup.log 2&amp;gt;&amp;amp;1
&lt;span class="c"&gt;# Renew Let's Encrypt certs twice a day&lt;/span&gt;
17 3,15 &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; /usr/bin/certbot renew &lt;span class="nt"&gt;--quiet&lt;/span&gt; &lt;span class="nt"&gt;--deploy-hook&lt;/span&gt; &lt;span class="s2"&gt;"systemctl reload nginx"&lt;/span&gt;
&lt;span class="c"&gt;# Health check every 5 minutes&lt;/span&gt;
&lt;span class="k"&gt;*&lt;/span&gt;/5 &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; /usr/local/bin/healthcheck.sh &lt;span class="o"&gt;||&lt;/span&gt; /usr/local/bin/slack-alert.sh &lt;span class="s2"&gt;"API down"&lt;/span&gt;
&lt;span class="c"&gt;# Clean Docker dangling images weekly&lt;/span&gt;
30 4 &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; 1 /usr/bin/docker system prune &lt;span class="nt"&gt;-af&lt;/span&gt; &lt;span class="nt"&gt;--filter&lt;/span&gt; &lt;span class="s2"&gt;"until=168h"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /var/log/docker-prune.log 2&amp;gt;&amp;amp;1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Best Practices and Common Mistakes
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;PATH is minimal.&lt;/strong&gt; Cron's default &lt;code&gt;PATH&lt;/code&gt; is just &lt;code&gt;/usr/bin:/bin&lt;/code&gt;. Your &lt;code&gt;docker&lt;/code&gt;, &lt;code&gt;aws&lt;/code&gt;, &lt;code&gt;pg_dump&lt;/code&gt;, &lt;code&gt;node&lt;/code&gt; may not be found. Set &lt;code&gt;PATH=&lt;/code&gt; at the top of the crontab or use full paths.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No shell aliases / .bashrc.&lt;/strong&gt; Cron runs &lt;code&gt;/bin/sh&lt;/code&gt; non-interactively — no rc files are sourced.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Working directory is &lt;code&gt;$HOME&lt;/code&gt;.&lt;/strong&gt; Use absolute paths or &lt;code&gt;cd /opt/app &amp;amp;&amp;amp;&lt;/code&gt; first.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Always redirect output.&lt;/strong&gt; Unredirected stdout/stderr becomes mail to the user. Use &lt;code&gt;&amp;gt;&amp;gt; /var/log/myjob.log 2&amp;gt;&amp;amp;1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use scripts, not inline one-liners&lt;/strong&gt; — testable and version-controllable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Idempotency / overlap.&lt;/strong&gt; Use a lockfile: &lt;code&gt;flock -n /tmp/myjob.lock -c '/path/to/job.sh'&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;%&lt;/code&gt; is special&lt;/strong&gt; — a literal &lt;code&gt;%&lt;/code&gt; must be escaped as &lt;code&gt;\%&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; The #1 cron PATH gotcha: your script works when you run &lt;code&gt;./backup.sh&lt;/code&gt; by hand, but cron says &lt;code&gt;docker: command not found&lt;/code&gt;. That's because your shell's PATH includes &lt;code&gt;/usr/local/bin&lt;/code&gt; but cron's doesn't. Always set &lt;code&gt;PATH=&lt;/code&gt; at the top of the crontab OR call binaries by absolute path.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The at Command
&lt;/h2&gt;

&lt;p&gt;Where &lt;code&gt;cron&lt;/code&gt; is for &lt;strong&gt;recurring&lt;/strong&gt; jobs, &lt;code&gt;at&lt;/code&gt; is for &lt;strong&gt;one-shot&lt;/strong&gt; jobs. "Restart this service at 3 AM tonight." "Run this cleanup once, ninety minutes from now." Then forget it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; at
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable&lt;/span&gt; &lt;span class="nt"&gt;--now&lt;/span&gt; atd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Basic Usage
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;at now + 5 minutes
at&amp;gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Hello from the future"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /tmp/hello.txt
at&amp;gt; systemctl restart nginx
at&amp;gt; ^D
job 3 at Tue May 26 11:47:00 2026
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Time formats &lt;code&gt;at&lt;/code&gt; understands: &lt;code&gt;now + 2 hours&lt;/code&gt;, &lt;code&gt;now + 1 day&lt;/code&gt;, &lt;code&gt;14:30&lt;/code&gt;, &lt;code&gt;14:30 tomorrow&lt;/code&gt;, &lt;code&gt;midnight&lt;/code&gt;, &lt;code&gt;noon&lt;/code&gt;, &lt;code&gt;teatime&lt;/code&gt; (16:00), &lt;code&gt;3pm Friday&lt;/code&gt;, &lt;code&gt;02:00 next month&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Piping Commands In
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"/opt/scripts/restart-app.sh"&lt;/span&gt; | at now + 1 hour
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"shutdown -h now"&lt;/span&gt; | &lt;span class="nb"&gt;sudo &lt;/span&gt;at 23:00
&lt;span class="nv"&gt;$ &lt;/span&gt;at &lt;span class="nt"&gt;-f&lt;/span&gt; /opt/scripts/cleanup.sh now + 30 minutes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Managing Jobs
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;atq                       &lt;span class="c"&gt;# list pending jobs&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;at &lt;span class="nt"&gt;-c&lt;/span&gt; 7                   &lt;span class="c"&gt;# show contents of job 7&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;atrm 7                    &lt;span class="c"&gt;# cancel job 7&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;batch&lt;/code&gt; is &lt;code&gt;at&lt;/code&gt;'s sibling: it runs queued commands only when the system load average drops below 1.5 — great for heavy one-off jobs.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Need&lt;/th&gt;
&lt;th&gt;Use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Run every day / hour / week&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;cron&lt;/code&gt; (or systemd timer)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Run exactly once, in the future&lt;/td&gt;
&lt;td&gt;&lt;code&gt;at&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"Restart server in 5 min if I don't cancel"&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;at&lt;/code&gt; (great safety net during config changes)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Run a heavy job when the system is idle&lt;/td&gt;
&lt;td&gt;&lt;code&gt;batch&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Retries, dependencies, complex orchestration&lt;/td&gt;
&lt;td&gt;systemd timer, Airflow/Argo&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; The "dead man's switch" trick: before editing risky SSH/firewall config, schedule a rollback: &lt;code&gt;echo "cp /etc/ssh/sshd_config.bak /etc/ssh/sshd_config &amp;amp;&amp;amp; systemctl restart ssh" | sudo at now + 5 minutes&lt;/code&gt;. Make your changes and test. If you lock yourself out, the job revokes them in 5 minutes. If everything works: &lt;code&gt;sudo atrm &amp;lt;jobnum&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  systemd Timers
&lt;/h2&gt;

&lt;p&gt;systemd timers are the &lt;strong&gt;modern replacement for cron&lt;/strong&gt;. More verbose to set up, but they integrate with the rest of systemd: structured logging in the journal, dependency management, resource limits, retries, and the ability to catch up after downtime.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Key idea — a timer is a unit that &lt;em&gt;triggers another unit&lt;/em&gt;. You always create TWO files: &lt;code&gt;foo.timer&lt;/code&gt; (when to run) and &lt;code&gt;foo.service&lt;/code&gt; (what to run).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  A Complete Example: Daily DB Backup
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="c"&gt;# /etc/systemd/system/myapp-backup.service
&lt;/span&gt;&lt;span class="nn"&gt;[Unit]&lt;/span&gt;
&lt;span class="py"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Daily backup of myapp Postgres database&lt;/span&gt;
&lt;span class="py"&gt;Wants&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;network-online.target&lt;/span&gt;
&lt;span class="py"&gt;After&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;network-online.target postgresql.service&lt;/span&gt;

&lt;span class="nn"&gt;[Service]&lt;/span&gt;
&lt;span class="py"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;oneshot&lt;/span&gt;
&lt;span class="py"&gt;User&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;postgres&lt;/span&gt;
&lt;span class="py"&gt;ExecStart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/usr/local/bin/backup-myapp.sh&lt;/span&gt;
&lt;span class="py"&gt;StandardOutput&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;journal&lt;/span&gt;
&lt;span class="py"&gt;StandardError&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;journal&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="c"&gt;# /etc/systemd/system/myapp-backup.timer
&lt;/span&gt;&lt;span class="nn"&gt;[Unit]&lt;/span&gt;
&lt;span class="py"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Run myapp DB backup daily at 02:00&lt;/span&gt;

&lt;span class="nn"&gt;[Timer]&lt;/span&gt;
&lt;span class="py"&gt;OnCalendar&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;*-*-* 02:00:00&lt;/span&gt;
&lt;span class="py"&gt;Persistent&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;span class="py"&gt;RandomizedDelaySec&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;5min&lt;/span&gt;
&lt;span class="py"&gt;Unit&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;myapp-backup.service&lt;/span&gt;

&lt;span class="nn"&gt;[Install]&lt;/span&gt;
&lt;span class="py"&gt;WantedBy&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;timers.target&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl daemon-reload
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable&lt;/span&gt; &lt;span class="nt"&gt;--now&lt;/span&gt; myapp-backup.timer   &lt;span class="c"&gt;# enable the TIMER, not the service&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;systemctl list-timers myapp-backup.timer
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl start myapp-backup.service        &lt;span class="c"&gt;# manually trigger once to test&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;journalctl &lt;span class="nt"&gt;-u&lt;/span&gt; myapp-backup.service &lt;span class="nt"&gt;-n&lt;/span&gt; 50          &lt;span class="c"&gt;# view output&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  OnCalendar Syntax
&lt;/h3&gt;

&lt;p&gt;Format: &lt;code&gt;DayOfWeek Year-Month-Day Hour:Minute:Second&lt;/code&gt;.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;code&gt;OnCalendar=&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;When&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;hourly&lt;/code&gt; / &lt;code&gt;daily&lt;/code&gt; / &lt;code&gt;weekly&lt;/code&gt; / &lt;code&gt;monthly&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Common shortcuts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;*-*-* 02:00:00&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Every day at 02:00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Mon-Fri 09:00&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Weekdays at 09:00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;*-*-01 04:00:00&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1st of every month at 04:00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;*-*-* *:0/15:00&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Every 15 minutes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;2026-12-31 23:59:00&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Once: New Year's Eve 2026&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Always validate before deploying:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;systemd-analyze calendar &lt;span class="s2"&gt;"Mon-Fri 09:00"&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;systemd-analyze calendar &lt;span class="nt"&gt;--iterations&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5 &lt;span class="s2"&gt;"*-*-* *:0/15:00"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Other Trigger Types
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Directive&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;OnBootSec=15min&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;15 minutes after boot&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;OnUnitActiveSec=1h&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1 hour after the service last activated&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Persistent=true&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;If a run was missed (server off), run on next boot&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RandomizedDelaySec=5min&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Spread a fleet of identical jobs — prevents thundering herd&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;systemctl list-timers              &lt;span class="c"&gt;# next/last run for every timer&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;systemctl list-timers &lt;span class="nt"&gt;--all&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;systemctl &lt;span class="nb"&gt;cat &lt;/span&gt;myapp-backup.timer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Cron vs systemd Timer
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;cron&lt;/th&gt;
&lt;th&gt;systemd timer&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Setup effort&lt;/td&gt;
&lt;td&gt;One line&lt;/td&gt;
&lt;td&gt;Two unit files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Logs&lt;/td&gt;
&lt;td&gt;Cron log + your redirects&lt;/td&gt;
&lt;td&gt;Centralized journal — &lt;code&gt;journalctl -u name&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dependencies&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;After=&lt;/code&gt;, &lt;code&gt;Wants=&lt;/code&gt;, &lt;code&gt;Requires=&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Retries on failure&lt;/td&gt;
&lt;td&gt;Write your own&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Restart=on-failure&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Catch up missed runs&lt;/td&gt;
&lt;td&gt;anacron only, blunt&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Persistent=true&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Resource limits&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;MemoryMax=&lt;/code&gt;, &lt;code&gt;CPUQuota=&lt;/code&gt;, etc.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Test/dry-run&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;systemd-analyze calendar "..."&lt;/code&gt; + &lt;code&gt;systemctl start&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Familiarity&lt;/td&gt;
&lt;td&gt;Universal&lt;/td&gt;
&lt;td&gt;Linux-only&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Migration tip: if you already have a cron job, you can convert it to a timer in 10 minutes. Bonus: &lt;code&gt;journalctl -u myjob&lt;/code&gt; gives you every run, exit code, and stdout/stderr without setting up your own log redirection. That alone justifies the two extra files.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Linux Security
&lt;/h2&gt;

&lt;p&gt;Security in Linux is not a feature you turn on, it's a posture you maintain. As a DevOps engineer you're responsible for hardening servers, protecting credentials, and reducing attack surface.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Security is a habit, not an event. Check daily: who is logging in, what ports are open, are the latest patches applied. Hardening once and forgetting is the biggest mistake — attackers only need one small gap.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Strong Passwords (PAM Password Quality)
&lt;/h3&gt;

&lt;p&gt;PAM is the framework Linux uses for authentication. The &lt;code&gt;pam_pwquality&lt;/code&gt; module enforces strong passwords system-wide via &lt;code&gt;/etc/security/pwquality.conf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="py"&gt;minlen&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;14&lt;/span&gt;
&lt;span class="py"&gt;minclass&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;4           # uppercase + lowercase + digit + symbol&lt;/span&gt;
&lt;span class="py"&gt;dcredit&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;-1           # require at least 1 digit&lt;/span&gt;
&lt;span class="py"&gt;ucredit&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;-1           # require at least 1 uppercase&lt;/span&gt;
&lt;span class="py"&gt;ocredit&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;-1           # require at least 1 symbol&lt;/span&gt;
&lt;span class="py"&gt;retry&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;3&lt;/span&gt;
&lt;span class="py"&gt;enforce_for_root&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Account lockout after failed attempts via &lt;code&gt;pam_faillock&lt;/code&gt; in &lt;code&gt;/etc/pam.d/common-auth&lt;/code&gt; (deny=5, unlock_time=900).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; Test before you log out! A bad PAM config can lock you out of your own server. Always keep a second SSH session open while editing PAM files.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  SSH Hardening Checklist (Recap)
&lt;/h3&gt;

&lt;p&gt;Edit &lt;code&gt;/etc/ssh/sshd_config&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="err"&gt;PermitRootLogin&lt;/span&gt; &lt;span class="err"&gt;no&lt;/span&gt;                 &lt;span class="c"&gt;# disable root login
&lt;/span&gt;&lt;span class="err"&gt;PasswordAuthentication&lt;/span&gt; &lt;span class="err"&gt;no&lt;/span&gt;          &lt;span class="c"&gt;# keys only — most important setting
&lt;/span&gt;&lt;span class="err"&gt;Port&lt;/span&gt; &lt;span class="err"&gt;2222&lt;/span&gt;                          &lt;span class="c"&gt;# reduce noise
&lt;/span&gt;&lt;span class="err"&gt;AllowUsers&lt;/span&gt; &lt;span class="err"&gt;deploy&lt;/span&gt; &lt;span class="err"&gt;ops-alice&lt;/span&gt;
&lt;span class="err"&gt;MaxAuthTries&lt;/span&gt; &lt;span class="err"&gt;3&lt;/span&gt;
&lt;span class="err"&gt;LoginGraceTime&lt;/span&gt; &lt;span class="err"&gt;30&lt;/span&gt;
&lt;span class="err"&gt;ClientAliveInterval&lt;/span&gt; &lt;span class="err"&gt;300&lt;/span&gt;
&lt;span class="err"&gt;X11Forwarding&lt;/span&gt; &lt;span class="err"&gt;no&lt;/span&gt;
&lt;span class="err"&gt;PermitEmptyPasswords&lt;/span&gt; &lt;span class="err"&gt;no&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;ssh-keygen &lt;span class="nt"&gt;-t&lt;/span&gt; ed25519 &lt;span class="nt"&gt;-a&lt;/span&gt; 100 &lt;span class="nt"&gt;-C&lt;/span&gt; &lt;span class="s2"&gt;"pushkar@workstation"&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;sshd &lt;span class="nt"&gt;-t&lt;/span&gt;                      &lt;span class="c"&gt;# test config syntax&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl reload ssh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Firewall
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# ufw — Ubuntu's friendly front-end&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw default deny incoming
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw default allow outgoing
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw allow 2222/tcp comment &lt;span class="s1"&gt;'SSH'&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw allow 80,443/tcp
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw allow from 192.168.1.0/24 to any port 5432   &lt;span class="c"&gt;# Postgres LAN only&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw limit 2222/tcp           &lt;span class="c"&gt;# rate-limit SSH&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw &lt;span class="nb"&gt;enable&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw status verbose
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Firewall&lt;/th&gt;
&lt;th&gt;Used By&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ufw&lt;/td&gt;
&lt;td&gt;Ubuntu, Debian&lt;/td&gt;
&lt;td&gt;Simple front-end to iptables/nftables&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;iptables&lt;/td&gt;
&lt;td&gt;Legacy everywhere&lt;/td&gt;
&lt;td&gt;Powerful, being replaced by nftables&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;nftables&lt;/td&gt;
&lt;td&gt;Modern kernel default&lt;/td&gt;
&lt;td&gt;Unified, faster, atomic rules&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;firewalld&lt;/td&gt;
&lt;td&gt;RHEL, Fedora&lt;/td&gt;
&lt;td&gt;Zone-based, runtime + permanent&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# firewalld (RHEL/Rocky)&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;firewall-cmd &lt;span class="nt"&gt;--add-service&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https &lt;span class="nt"&gt;--permanent&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;firewall-cmd &lt;span class="nt"&gt;--add-port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2222/tcp &lt;span class="nt"&gt;--permanent&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;firewall-cmd &lt;span class="nt"&gt;--reload&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  fail2ban — Intrusion Prevention
&lt;/h3&gt;

&lt;p&gt;fail2ban scans log files and bans IPs that show malicious behaviour by updating firewall rules dynamically.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="c"&gt;# /etc/fail2ban/jail.local
&lt;/span&gt;&lt;span class="nn"&gt;[sshd]&lt;/span&gt;
&lt;span class="py"&gt;enabled&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;true&lt;/span&gt;
&lt;span class="py"&gt;port&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;2222&lt;/span&gt;
&lt;span class="py"&gt;maxretry&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;3&lt;/span&gt;
&lt;span class="py"&gt;bantime&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;24h&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable&lt;/span&gt; &lt;span class="nt"&gt;--now&lt;/span&gt; fail2ban
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;fail2ban-client status sshd
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;fail2ban-client &lt;span class="nb"&gt;set &lt;/span&gt;sshd unbanip 203.0.113.45
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Real world:&lt;/strong&gt; On any internet-facing VM you'll see SSH brute-force attempts within minutes of booting. fail2ban + non-standard port + key-only auth reduces auth.log from thousands of failure lines per day to near-zero. It's the single highest-leverage hardening step.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  SELinux vs AppArmor — Mandatory Access Control
&lt;/h3&gt;

&lt;p&gt;Standard Unix permissions are &lt;strong&gt;discretionary&lt;/strong&gt; (the owner decides access). MAC adds a second layer: even root cannot violate kernel-enforced policies.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;SELinux&lt;/th&gt;
&lt;th&gt;AppArmor&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Default on&lt;/td&gt;
&lt;td&gt;RHEL, Fedora, CentOS&lt;/td&gt;
&lt;td&gt;Ubuntu, Debian, SUSE&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Policy style&lt;/td&gt;
&lt;td&gt;Labels on inodes&lt;/td&gt;
&lt;td&gt;Path-based profiles&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Granularity&lt;/td&gt;
&lt;td&gt;Very fine-grained&lt;/td&gt;
&lt;td&gt;Easier to read &amp;amp; write&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# SELinux&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;getenforce                       &lt;span class="c"&gt;# Enforcing | Permissive | Disabled&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;setenforce 0                &lt;span class="c"&gt;# Permissive (logs only)&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-Z&lt;/span&gt; /var/www/html              &lt;span class="c"&gt;# show SELinux contexts&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;restorecon &lt;span class="nt"&gt;-Rv&lt;/span&gt; /var/www/html

&lt;span class="c"&gt;# AppArmor&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;aa-status
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;aa-complain /etc/apparmor.d/usr.bin.nginx   &lt;span class="c"&gt;# learning mode&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;aa-enforce  /etc/apparmor.d/usr.bin.nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; Don't disable MAC because "the app won't work." Set it to Permissive/Complain mode, reproduce the issue, read the audit log, then write a targeted policy exception. Disabling SELinux/AppArmor in production is a major red flag in security audits.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  File Integrity — Hashes
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;md5sum &lt;/span&gt;ubuntu-24.04.iso          &lt;span class="c"&gt;# FAST but cryptographically broken — checksums only&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sha256sum &lt;/span&gt;ubuntu-24.04.iso       &lt;span class="c"&gt;# the standard for downloads&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sha512sum &lt;/span&gt;kernel.tar.xz          &lt;span class="c"&gt;# extra paranoid&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sha256sum&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; SHA256SUMS          &lt;span class="c"&gt;# verify against a published checksum file&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Need to hash a string or small file quickly, or compare a download against its published digest without dropping to a shell? The &lt;a href="https://opscanopy.com/hash-generator/" rel="noopener noreferrer"&gt;Hash Generator&lt;/a&gt; produces MD5/SHA-1/SHA-256/SHA-512 in the browser.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  GPG Basics
&lt;/h3&gt;

&lt;p&gt;GPG provides encryption, signing, and key management — used everywhere from APT repo signing to package release verification.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;gpg &lt;span class="nt"&gt;--full-generate-key&lt;/span&gt;          &lt;span class="c"&gt;# use ed25519 or RSA 4096&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;gpg &lt;span class="nt"&gt;--encrypt&lt;/span&gt; &lt;span class="nt"&gt;--armor&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; alice@example.com secrets.txt
&lt;span class="nv"&gt;$ &lt;/span&gt;gpg &lt;span class="nt"&gt;--decrypt&lt;/span&gt; secrets.txt.asc &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; secrets.txt
&lt;span class="nv"&gt;$ &lt;/span&gt;gpg &lt;span class="nt"&gt;--detach-sign&lt;/span&gt; &lt;span class="nt"&gt;--armor&lt;/span&gt; release-1.4.0.tar.gz
&lt;span class="nv"&gt;$ &lt;/span&gt;gpg &lt;span class="nt"&gt;--verify&lt;/span&gt; kubectl.sha256.sig kubectl.sha256   &lt;span class="c"&gt;# verify upstream releases&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Finding Privilege-Escalation Vectors
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;find / &lt;span class="nt"&gt;-perm&lt;/span&gt; &lt;span class="nt"&gt;-4000&lt;/span&gt; &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="nt"&gt;-xdev&lt;/span&gt; 2&amp;gt;/dev/null   &lt;span class="c"&gt;# SUID binaries&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;find / &lt;span class="nt"&gt;-perm&lt;/span&gt; &lt;span class="nt"&gt;-2000&lt;/span&gt; &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="nt"&gt;-xdev&lt;/span&gt; 2&amp;gt;/dev/null   &lt;span class="c"&gt;# SGID binaries&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;find / &lt;span class="nt"&gt;-perm&lt;/span&gt; &lt;span class="nt"&gt;-0002&lt;/span&gt; &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="nt"&gt;-xdev&lt;/span&gt; 2&amp;gt;/dev/null   &lt;span class="c"&gt;# world-writable files (red flag)&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;find / &lt;span class="nt"&gt;-nouser&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nt"&gt;-nogroup&lt;/span&gt; 2&amp;gt;/dev/null         &lt;span class="c"&gt;# files with no owner&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Automated Updates
&lt;/h3&gt;

&lt;p&gt;Unpatched systems are the #1 source of compromise. Automate it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Ubuntu&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;unattended-upgrades apt-listchanges
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dpkg-reconfigure &lt;span class="nt"&gt;--priority&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;low unattended-upgrades
&lt;span class="c"&gt;# RHEL&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install &lt;/span&gt;dnf-automatic
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable&lt;/span&gt; &lt;span class="nt"&gt;--now&lt;/span&gt; dnf-automatic.timer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Real DevOps Security Checklist
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Update OS and patches&lt;/strong&gt; — unattended-upgrades / dnf-automatic; reboot for kernel CVEs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Disable root SSH&lt;/strong&gt; and use &lt;strong&gt;key-only authentication&lt;/strong&gt; (ed25519).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Firewall default deny&lt;/strong&gt; — open only required ports.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;fail2ban&lt;/strong&gt; on SSH and other internet-facing services.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Audit users&lt;/strong&gt; — remove unused accounts, lock service accounts, rotate keys.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Disable unused services&lt;/strong&gt; — &lt;code&gt;systemctl list-unit-files --state=enabled&lt;/code&gt;, mask what you don't need.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Centralized logs&lt;/strong&gt; — ship to ELK/Loki/CloudWatch; local logs can be wiped by attackers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Time sync&lt;/strong&gt; — chrony or systemd-timesyncd; broken clocks break TLS and audit timestamps.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SELinux/AppArmor enforcing&lt;/strong&gt; — never permanently disable MAC.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vulnerability scans&lt;/strong&gt; — Lynis (&lt;code&gt;sudo lynis audit system&lt;/code&gt;), OpenSCAP, Trivy for containers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backups + tested restore&lt;/strong&gt; — an untested backup is a hope, not a backup.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Least privilege&lt;/strong&gt; — sudo whitelists, no shared accounts, separate prod/staging credentials.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Container security&lt;/strong&gt; — non-root USER, drop capabilities, read-only rootfs, scan images (Trivy/Grype), use distroless/minimal base images.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Secrets management&lt;/strong&gt; — Vault, AWS Secrets Manager, sealed-secrets; never commit secrets to git.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitor auth events&lt;/strong&gt; — alert on failed sudo, new SSH keys, sudoers changes.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Real world:&lt;/strong&gt; When you join a new DevOps team, run Lynis on a staging box in week one. It generates a 100+ point hardening report. Even mature shops typically score 60-70/100 — the gap is your low-hanging-fruit roadmap and an instant credibility builder.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Troubleshooting Methodology
&lt;/h2&gt;

&lt;p&gt;Troubleshooting is the skill that separates a junior DevOps from a senior. Tools you can google. Methodology you have to internalise.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Systematic Approach
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Define the problem clearly.&lt;/strong&gt; "The website is slow" is not a definition. "p95 latency on /api/checkout went from 200ms to 3s at 14:02 UTC for all users" is.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gather data.&lt;/strong&gt; Logs, metrics, kernel messages (&lt;code&gt;dmesg&lt;/code&gt;), recent changes (deploys, config), system state (&lt;code&gt;top&lt;/code&gt;, &lt;code&gt;df&lt;/code&gt;, &lt;code&gt;ss&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reproduce it.&lt;/strong&gt; Either in staging, or get exact steps.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Form a hypothesis.&lt;/strong&gt; One thing at a time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test with the smallest change.&lt;/strong&gt; Don't change five things at once.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Document.&lt;/strong&gt; Symptom, root cause, fix, prevention.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The biggest mistake is skipping root cause in the rush to fix. A server restart gives 5 minutes of relief, but if you didn't catch the root cause the issue returns in 3 days — and by then the logs have rotated. Slow down to go fast.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Where to Start, by Symptom
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Symptom Area&lt;/th&gt;
&lt;th&gt;First Places to Look&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Boot issues&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;journalctl -b&lt;/code&gt;, &lt;code&gt;journalctl -b -1&lt;/code&gt;, &lt;code&gt;dmesg&lt;/code&gt;, &lt;code&gt;systemd-analyze blame&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Auth / SSH&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;/var/log/auth.log&lt;/code&gt; (or &lt;code&gt;/var/log/secure&lt;/code&gt;), &lt;code&gt;journalctl -u sshd&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Service problem&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;systemctl status &amp;lt;svc&amp;gt;&lt;/code&gt;, &lt;code&gt;journalctl -u &amp;lt;svc&amp;gt; -n 200&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Disk&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;df -h&lt;/code&gt;, &lt;code&gt;df -i&lt;/code&gt;, &lt;code&gt;du -sh /*&lt;/code&gt;, &lt;code&gt;lsblk -f&lt;/code&gt;, &lt;code&gt;iostat -xz 1&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Network&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ip a&lt;/code&gt;, &lt;code&gt;ip r&lt;/code&gt;, &lt;code&gt;curl -v&lt;/code&gt;, &lt;code&gt;ss -tulpn&lt;/code&gt;, &lt;code&gt;dig&lt;/code&gt;, &lt;code&gt;tcpdump&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Performance&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;top&lt;/code&gt;, &lt;code&gt;vmstat 1&lt;/code&gt;, &lt;code&gt;iostat -xz 1&lt;/code&gt;, &lt;code&gt;sar -u 1 5&lt;/code&gt;, &lt;code&gt;pidstat 1&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Memory / OOM&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;free -h&lt;/code&gt;, `dmesg \&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Common Troubleshooting Scenarios
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Server slow / not responding:&lt;/strong&gt;&lt;br&gt;
{% raw %}&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;uptime&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; top &lt;span class="nt"&gt;-o&lt;/span&gt; %CPU&lt;span class="p"&gt;;&lt;/span&gt; top &lt;span class="nt"&gt;-o&lt;/span&gt; %MEM&lt;span class="p"&gt;;&lt;/span&gt; iostat &lt;span class="nt"&gt;-xz&lt;/span&gt; 1 5&lt;span class="p"&gt;;&lt;/span&gt; vmstat 1 5&lt;span class="p"&gt;;&lt;/span&gt; ss &lt;span class="nt"&gt;-s&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Identify the bottleneck (CPU / IO / memory / network), then kill/throttle the offender, scale resources, or add caching. 80% of "server slow" tickets are: a runaway log filling disk, a query without an index, or memory pressure causing swap thrashing — check disk and swap first.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Service won't start:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl status nginx
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;journalctl &lt;span class="nt"&gt;-u&lt;/span&gt; nginx &lt;span class="nt"&gt;-n&lt;/span&gt; 100 &lt;span class="nt"&gt;--no-pager&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nginx &lt;span class="nt"&gt;-t&lt;/span&gt;                  &lt;span class="c"&gt;# config syntax check&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;ss &lt;span class="nt"&gt;-tulpn&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; :80      &lt;span class="c"&gt;# port already in use?&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Common causes: config syntax error, port already bound, missing dependency in &lt;code&gt;After=&lt;/code&gt;, wrong &lt;code&gt;User=&lt;/code&gt;, SELinux/AppArmor denial.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SSH "Permission denied (publickey)":&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;ssh &lt;span class="nt"&gt;-vvv&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; ~/.ssh/id_ed25519 deploy@server     &lt;span class="c"&gt;# which key was tried?&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;stat&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'%a %U %G %n'&lt;/span&gt; ~/.ssh ~/.ssh/authorized_keys
&lt;span class="c"&gt;# Required: ~ = 755/750, .ssh = 700, authorized_keys = 600, owned by the user&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;SSH "Connection refused" vs "timed out":&lt;/strong&gt; Refused = something at the destination actively rejected (sshd down, or firewall REJECT). Timed out = packets never arrived (routing, cloud security group, or a DROP rule). AWS/GCP/Azure security groups are the most common culprit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Out of memory (OOM killer):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;dmesg &lt;span class="nt"&gt;-T&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;'killed process'&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;journalctl &lt;span class="nt"&gt;-k&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;'oom-killer'&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;free &lt;span class="nt"&gt;-h&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;cat&lt;/span&gt; /proc/swaps
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fix: add swap (short term), increase memory, fix the leaking app, or set &lt;code&gt;MemoryMax=&lt;/code&gt; in the systemd unit. Java/Node in containers often get OOM-killed because they read the host's memory, not the cgroup limit — set &lt;code&gt;-Xmx&lt;/code&gt; or &lt;code&gt;NODE_OPTIONS=--max-old-space-size&lt;/code&gt; to match the container limit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Container not starting:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker ps &lt;span class="nt"&gt;-a&lt;/span&gt;                           &lt;span class="c"&gt;# find the dead container's exit code&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker logs &lt;span class="nt"&gt;--tail&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;200 myapp
&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;--entrypoint&lt;/span&gt; sh myapp:latest   &lt;span class="c"&gt;# poke inside&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Common exit codes: 0 = clean exit, 1 = app error, 125 = docker daemon error, 126 = not executable, 127 = command not found, 137 = OOM-killed, 139 = segfault, 143 = SIGTERM. Volume permission mismatch (UID inside container vs host) is the #1 silent failure in dev environments.&lt;/p&gt;

&lt;h3&gt;
  
  
  Power Tools
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Use For&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;strace&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Trace system calls&lt;/td&gt;
&lt;td&gt;&lt;code&gt;strace -f -e openat,connect -p 1234&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;lsof&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List open files, sockets, FDs&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;lsof -i :8080&lt;/code&gt;, &lt;code&gt;lsof +L1&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;perf&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;CPU profiling, flame graphs&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo perf top&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tcpdump&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Packet capture&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo tcpdump -i any -w cap.pcap port 443&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;bpftrace&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;eBPF tracing&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo execsnoop-bpfcc&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  The USE Method
&lt;/h3&gt;

&lt;p&gt;Brendan Gregg's USE method: for every resource (CPU, memory, disk IO, disk capacity, network IO, file descriptors), check &lt;strong&gt;U&lt;/strong&gt;tilization, &lt;strong&gt;S&lt;/strong&gt;aturation, and &lt;strong&gt;E&lt;/strong&gt;rrors.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Real world:&lt;/strong&gt; In every senior DevOps interview you'll get an open-ended scenario: "Production API is returning 500s, walk me through what you do." They're testing whether you have a &lt;em&gt;method&lt;/em&gt;: define → gather → hypothesise → test → document. Mention rollback as the first option before deep debugging in prod. That single framing puts you ahead of most candidates.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Common Interview Questions
&lt;/h2&gt;

&lt;p&gt;These are the highest-frequency Linux questions asked in DevOps and SRE interviews, condensed to the answer that signals real understanding. Drill them out loud — if you can teach the answer in your own words, you own it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Difference between Linux and Unix?&lt;/strong&gt; Unix is a proprietary OS family from the 1970s (AT&amp;amp;T Bell Labs); Linux is a free, open-source, Unix-like &lt;em&gt;kernel&lt;/em&gt; created by Linus Torvalds in 1991. Linux follows Unix design principles but shares no code with original Unix. macOS is certified Unix (BSD lineage); Linux is not.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Difference between the kernel and the operating system?&lt;/strong&gt; The kernel is the core program that talks to hardware and manages CPU, memory, devices, and processes. The OS is the kernel &lt;em&gt;plus&lt;/em&gt; userspace — shell, libraries, utilities — that makes it usable. Calling the kernel "the OS" is the classic blunder.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Difference between &lt;code&gt;bash&lt;/code&gt; and &lt;code&gt;sh&lt;/code&gt;?&lt;/strong&gt; &lt;code&gt;sh&lt;/code&gt; is the POSIX-standard minimal shell; &lt;code&gt;bash&lt;/code&gt; is a superset with arrays, &lt;code&gt;[[ ]]&lt;/code&gt;, brace expansion, and the &lt;code&gt;function&lt;/code&gt; keyword. On Debian/Ubuntu, &lt;code&gt;/bin/sh&lt;/code&gt; is &lt;code&gt;dash&lt;/code&gt;, not bash — so a &lt;code&gt;#!/bin/sh&lt;/code&gt; script using bash-only features works on RHEL but breaks on Ubuntu. Match your shebang to the features you use.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. How do Linux file permissions work?&lt;/strong&gt; Three triplets (owner/group/other), each with read (4), write (2), execute (1). Set symbolically (&lt;code&gt;chmod u+x&lt;/code&gt;) or octal (&lt;code&gt;chmod 755&lt;/code&gt;). Special bits: setuid (run as owner), setgid (inherit group / run as group), sticky (only owner can delete, as on &lt;code&gt;/tmp&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Hard link vs symbolic link?&lt;/strong&gt; A hard link is a second directory entry pointing to the &lt;em&gt;same inode&lt;/em&gt; — same FS only, can't link directories, survives deletion of any one name while link count &amp;gt; 0. A symlink is a separate file whose content is a path string, resolved at access time — can cross filesystems, can point to directories, dangles if the target is removed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. What happens from power-on to login prompt?&lt;/strong&gt; BIOS/UEFI → POST → MBR/GPT boot sector → GRUB loads kernel + initramfs → kernel mounts &lt;code&gt;/&lt;/code&gt; and starts PID 1 → systemd starts services in dependency order and mounts &lt;code&gt;/etc/fstab&lt;/code&gt; → reaches multi-user.target → login (getty/sshd). Most production outages happen here.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7. What's the difference between a VM and a container?&lt;/strong&gt; A VM virtualizes hardware and runs a full guest kernel. A container is a regular process on the host kernel isolated with namespaces (PID, net, mount, user) and cgroups (CPU, memory, I/O limits). Container = process; VM = virtual machine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;8. Difference between &lt;code&gt;kill -9&lt;/code&gt; and &lt;code&gt;kill -15&lt;/code&gt;?&lt;/strong&gt; &lt;code&gt;-15&lt;/code&gt; (SIGTERM) is a polite shutdown request the process can catch and clean up after. &lt;code&gt;-9&lt;/code&gt; (SIGKILL) is uncatchable and immediate — no cleanup, risk of corrupt state. Always try SIGTERM first.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;9. Explain &lt;code&gt;fork()&lt;/code&gt; and &lt;code&gt;exec()&lt;/code&gt;.&lt;/strong&gt; &lt;code&gt;fork()&lt;/code&gt; duplicates the current process (child gets a new PID). &lt;code&gt;exec()&lt;/code&gt; replaces the child's memory image with a new program (same PID). The shell uses fork+exec+wait to run every command.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;10. Disk shows full but &lt;code&gt;df&lt;/code&gt; reports free space — what's wrong?&lt;/strong&gt; Either inode exhaustion (&lt;code&gt;df -i&lt;/code&gt; shows 100% IUse) or a process holding a deleted-but-open file (&lt;code&gt;lsof | grep deleted&lt;/code&gt;). For inodes, delete the millions of tiny files; for the open file, restart the holding process.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;11. How do you debug a service that won't start?&lt;/strong&gt; &lt;code&gt;systemctl status &amp;lt;svc&amp;gt;&lt;/code&gt;, then &lt;code&gt;journalctl -u &amp;lt;svc&amp;gt; -n 100&lt;/code&gt;, then validate config (&lt;code&gt;nginx -t&lt;/code&gt;, &lt;code&gt;sshd -t&lt;/code&gt;), then check the port isn't already bound (&lt;code&gt;ss -tulpn&lt;/code&gt;). Common causes: config syntax, port conflict, wrong &lt;code&gt;User=&lt;/code&gt;, missing dependency, MAC denial.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;12. What's the difference between cron and a systemd timer?&lt;/strong&gt; Both schedule recurring work. systemd timers add journal logging, dependency ordering (&lt;code&gt;After=&lt;/code&gt;/&lt;code&gt;Wants=&lt;/code&gt;), retries (&lt;code&gt;Restart=&lt;/code&gt;), catch-up of missed runs (&lt;code&gt;Persistent=true&lt;/code&gt;), and resource limits. Cron is simpler and universal. Use timers when you need observability and dependencies.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;13. How do you harden SSH on a production server?&lt;/strong&gt; &lt;code&gt;PasswordAuthentication no&lt;/code&gt; (key-only), &lt;code&gt;PermitRootLogin no&lt;/code&gt;, restrict with &lt;code&gt;AllowUsers&lt;/code&gt;, lower &lt;code&gt;MaxAuthTries&lt;/code&gt;, non-standard port, add &lt;code&gt;fail2ban&lt;/code&gt;, and use ed25519 keys. Always &lt;code&gt;sshd -t&lt;/code&gt; and keep a second session open before reloading.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;14. A server is slow — walk me through it.&lt;/strong&gt; Triage in order: &lt;code&gt;uptime&lt;/code&gt; (load vs core count), &lt;code&gt;top&lt;/code&gt;/&lt;code&gt;htop&lt;/code&gt; (CPU/mem hogs), &lt;code&gt;free -h&lt;/code&gt; (look at &lt;code&gt;available&lt;/code&gt;, not &lt;code&gt;free&lt;/code&gt;), &lt;code&gt;iostat -xz 2&lt;/code&gt; (disk &lt;code&gt;%util&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt;), &lt;code&gt;ss -s&lt;/code&gt; (connections), &lt;code&gt;dmesg -T | tail&lt;/code&gt; (kernel/OOM). This is the USE method: Utilization, Saturation, Errors.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;15. How do you find what's listening on a port / using a port?&lt;/strong&gt; &lt;code&gt;ss -tlnp | grep :80&lt;/code&gt; or &lt;code&gt;lsof -i :80&lt;/code&gt; shows the listener and PID. To free it, identify the PID then stop that service (&lt;code&gt;fuser -k 80/tcp&lt;/code&gt; as a blunt instrument).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; In interviews, never just say "I don't know" — add "but here's how I'd find out." Confidence with humility beats fake expertise. For an experienced developer pivoting into DevOps, that maturity is a genuine differentiator.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Quick Reference Cheat Sheets
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Bookmark this section&lt;/em&gt; — these are the reference sheets you will hit Ctrl+F on during your first 6 months on the job. Print them, paste them on your wall, whatever works. Real production-grade reference, no fluff.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Top 100 Linux Commands (Grouped)
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Files &amp;amp; Directories
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ls&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List directory&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ls -lah --color&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cd&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Change directory&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;cd -&lt;/code&gt; (previous)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pwd&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print working dir&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;pwd -P&lt;/code&gt; (resolve symlinks)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mkdir&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Make directory&lt;/td&gt;
&lt;td&gt;&lt;code&gt;mkdir -p a/b/c&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rmdir&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove empty dir&lt;/td&gt;
&lt;td&gt;&lt;code&gt;rmdir empty_folder&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rm&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove files&lt;/td&gt;
&lt;td&gt;&lt;code&gt;rm -rf node_modules&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Copy&lt;/td&gt;
&lt;td&gt;&lt;code&gt;cp -av src/ dst/&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mv&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Move/rename&lt;/td&gt;
&lt;td&gt;&lt;code&gt;mv old.txt new.txt&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ln&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Link (hard/soft)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ln -s /opt/app /usr/local/app&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;touch&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create empty / update mtime&lt;/td&gt;
&lt;td&gt;&lt;code&gt;touch app.log&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;stat&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;File metadata&lt;/td&gt;
&lt;td&gt;&lt;code&gt;stat /etc/passwd&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;file&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Detect file type&lt;/td&gt;
&lt;td&gt;&lt;code&gt;file binary.bin&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;find&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Find files&lt;/td&gt;
&lt;td&gt;&lt;code&gt;find . -name "*.log" -mtime +7&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;locate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Fast filename search&lt;/td&gt;
&lt;td&gt;&lt;code&gt;locate sshd_config&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tree&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Directory tree&lt;/td&gt;
&lt;td&gt;&lt;code&gt;tree -L 2&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;basename&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Strip path&lt;/td&gt;
&lt;td&gt;&lt;code&gt;basename /a/b/c.txt&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dirname&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Strip filename&lt;/td&gt;
&lt;td&gt;&lt;code&gt;dirname /a/b/c.txt&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;realpath&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Resolve absolute path&lt;/td&gt;
&lt;td&gt;&lt;code&gt;realpath ./app&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  Text Processing
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cat&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print file&lt;/td&gt;
&lt;td&gt;&lt;code&gt;cat /etc/os-release&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tac&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Reverse cat&lt;/td&gt;
&lt;td&gt;&lt;code&gt;tac access.log&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;less&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Paged viewer&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;less +F app.log&lt;/code&gt; (follow)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;more&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Old pager&lt;/td&gt;
&lt;td&gt;&lt;code&gt;more big.txt&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;head&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;First N lines&lt;/td&gt;
&lt;td&gt;&lt;code&gt;head -n 20 file&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tail&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Last N lines&lt;/td&gt;
&lt;td&gt;&lt;code&gt;tail -f -n 100 app.log&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;grep&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pattern search&lt;/td&gt;
&lt;td&gt;`grep -rEn "TODO&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;{% raw %}&lt;code&gt;egrep / fgrep&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;ERE / fixed&lt;/td&gt;
&lt;td&gt;`grep -E "a&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;{% raw %}&lt;code&gt;sed&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Stream editor&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sed -i 's/dev/prod/g' app.conf&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;awk&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Field processor&lt;/td&gt;
&lt;td&gt;&lt;code&gt;awk '{print $1,$9}' access.log&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cut&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Extract columns&lt;/td&gt;
&lt;td&gt;&lt;code&gt;cut -d: -f1 /etc/passwd&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sort&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Sort lines&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sort -u -k2,2n&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;uniq&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Dedupe (sorted)&lt;/td&gt;
&lt;td&gt;`sort file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;{% raw %}&lt;code&gt;wc&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Count&lt;/td&gt;
&lt;td&gt;&lt;code&gt;wc -l *.py&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tr&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Translate chars&lt;/td&gt;
&lt;td&gt;&lt;code&gt;tr 'a-z' 'A-Z'&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tee&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Write &amp;amp; pass through&lt;/td&gt;
&lt;td&gt;`cmd&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;{% raw %}&lt;code&gt;diff&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Compare&lt;/td&gt;
&lt;td&gt;&lt;code&gt;diff -u a b&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;patch&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Apply diff&lt;/td&gt;
&lt;td&gt;&lt;code&gt;patch -p1 &amp;lt; fix.patch&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;jq&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;JSON processor&lt;/td&gt;
&lt;td&gt;&lt;code&gt;jq '.items[].name' data.json&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;yq&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;YAML processor&lt;/td&gt;
&lt;td&gt;&lt;code&gt;yq '.services' docker-compose.yml&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;xargs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Build commands&lt;/td&gt;
&lt;td&gt;`find . -name "*.bak"&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  Process
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;{% raw %}&lt;code&gt;ps&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Process snapshot&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ps -ef&lt;/code&gt; or &lt;code&gt;ps aux&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;top&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Live processes&lt;/td&gt;
&lt;td&gt;&lt;code&gt;top -o %CPU&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;htop&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Better top&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;htop&lt;/code&gt; (F6 sort, F9 signal)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;kill&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Send signal&lt;/td&gt;
&lt;td&gt;&lt;code&gt;kill -9 1234&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pkill&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Kill by name&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pkill -f gunicorn&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;killall&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Kill all by name&lt;/td&gt;
&lt;td&gt;&lt;code&gt;killall nginx&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pgrep&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Find PIDs&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pgrep -af java&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nice&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run with priority&lt;/td&gt;
&lt;td&gt;&lt;code&gt;nice -n 10 ./batch.sh&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;renice&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Change priority&lt;/td&gt;
&lt;td&gt;&lt;code&gt;renice 5 -p 1234&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nohup&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Survive logout&lt;/td&gt;
&lt;td&gt;&lt;code&gt;nohup ./run.sh &amp;amp;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;jobs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Shell jobs&lt;/td&gt;
&lt;td&gt;&lt;code&gt;jobs -l&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;bg / fg&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Background/foreground&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;bg %1&lt;/code&gt; / &lt;code&gt;fg %1&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;strace&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Trace syscalls&lt;/td&gt;
&lt;td&gt;&lt;code&gt;strace -p 1234 -f -e network&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;lsof&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Open files&lt;/td&gt;
&lt;td&gt;&lt;code&gt;lsof -i :8080&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  Network
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ip&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Modern net tool&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ip -br a&lt;/code&gt; / &lt;code&gt;ip r&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ifconfig&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Legacy&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ifconfig eth0&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ping&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;ICMP test&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ping -c 4 google.com&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;traceroute&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Hop trace&lt;/td&gt;
&lt;td&gt;&lt;code&gt;traceroute -n 8.8.8.8&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mtr&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Live traceroute&lt;/td&gt;
&lt;td&gt;&lt;code&gt;mtr 1.1.1.1&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dig&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;DNS lookup&lt;/td&gt;
&lt;td&gt;&lt;code&gt;dig +short A example.com&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nslookup&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;DNS query&lt;/td&gt;
&lt;td&gt;&lt;code&gt;nslookup example.com 8.8.8.8&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;host&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Simple DNS&lt;/td&gt;
&lt;td&gt;&lt;code&gt;host example.com&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;curl&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;HTTP client&lt;/td&gt;
&lt;td&gt;&lt;code&gt;curl -fsSL https://x.com&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;wget&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Download&lt;/td&gt;
&lt;td&gt;&lt;code&gt;wget -c https://x.com/file&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ss&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Sockets&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ss -tulnp&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;netstat&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Legacy ss&lt;/td&gt;
&lt;td&gt;&lt;code&gt;netstat -tulnp&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nc&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Netcat&lt;/td&gt;
&lt;td&gt;&lt;code&gt;nc -zv host 443&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nmap&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Port scanner&lt;/td&gt;
&lt;td&gt;&lt;code&gt;nmap -sV -p 1-1000 host&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tcpdump&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Packet capture&lt;/td&gt;
&lt;td&gt;&lt;code&gt;tcpdump -i eth0 port 80&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;iptables&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Firewall (legacy)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;iptables -L -n -v&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nft&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;nftables&lt;/td&gt;
&lt;td&gt;&lt;code&gt;nft list ruleset&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  System
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;uname&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Kernel info&lt;/td&gt;
&lt;td&gt;&lt;code&gt;uname -a&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;hostnamectl&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Hostname/OS info&lt;/td&gt;
&lt;td&gt;&lt;code&gt;hostnamectl&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;uptime&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Load avg&lt;/td&gt;
&lt;td&gt;&lt;code&gt;uptime&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;w / who&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Logged users&lt;/td&gt;
&lt;td&gt;&lt;code&gt;w&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;last&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Login history&lt;/td&gt;
&lt;td&gt;&lt;code&gt;last -n 20&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;date&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Date/time&lt;/td&gt;
&lt;td&gt;&lt;code&gt;date -u +%Y-%m-%dT%H:%M:%SZ&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;timedatectl&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Timezone/NTP&lt;/td&gt;
&lt;td&gt;&lt;code&gt;timedatectl set-timezone Asia/Kolkata&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;systemctl&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Service control&lt;/td&gt;
&lt;td&gt;&lt;code&gt;systemctl status nginx&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;journalctl&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Logs&lt;/td&gt;
&lt;td&gt;&lt;code&gt;journalctl -u nginx -f&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dmesg&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Kernel ring buffer&lt;/td&gt;
&lt;td&gt;`dmesg -T&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;{% raw %}&lt;code&gt;free&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Memory&lt;/td&gt;
&lt;td&gt;&lt;code&gt;free -h&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;vmstat&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Virtual memory stats&lt;/td&gt;
&lt;td&gt;&lt;code&gt;vmstat 1 5&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;iostat&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;IO stats&lt;/td&gt;
&lt;td&gt;&lt;code&gt;iostat -xz 1&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sar&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Historical perf&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sar -u 1 5&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;env&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Env vars&lt;/td&gt;
&lt;td&gt;`env&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;{% raw %}&lt;code&gt;which / type&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Locate binary&lt;/td&gt;
&lt;td&gt;&lt;code&gt;type -a ls&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  Security &amp;amp; Users
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Privilege escalation&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo -i&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;su&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Switch user&lt;/td&gt;
&lt;td&gt;&lt;code&gt;su - deploy&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;passwd&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Change password&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo passwd alice&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;useradd&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Add user&lt;/td&gt;
&lt;td&gt;&lt;code&gt;useradd -m -s /bin/bash alice&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;usermod&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Modify user&lt;/td&gt;
&lt;td&gt;&lt;code&gt;usermod -aG docker alice&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;userdel&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Delete user&lt;/td&gt;
&lt;td&gt;&lt;code&gt;userdel -r alice&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;groupadd&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Add group&lt;/td&gt;
&lt;td&gt;&lt;code&gt;groupadd devops&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show IDs&lt;/td&gt;
&lt;td&gt;&lt;code&gt;id alice&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;chage&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pass aging&lt;/td&gt;
&lt;td&gt;&lt;code&gt;chage -l alice&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;getfacl/setfacl&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;ACLs&lt;/td&gt;
&lt;td&gt;&lt;code&gt;setfacl -m u:alice:rw file&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;fail2ban-client&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Ban abusers&lt;/td&gt;
&lt;td&gt;&lt;code&gt;fail2ban-client status sshd&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ufw / firewalld&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Firewall frontends&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ufw allow 22/tcp&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;openssl&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Crypto Swiss-army&lt;/td&gt;
&lt;td&gt;&lt;code&gt;openssl s_client -connect host:443&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  Compression
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tar&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Archive&lt;/td&gt;
&lt;td&gt;&lt;code&gt;tar -czvf a.tgz dir/&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tar -x&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Extract&lt;/td&gt;
&lt;td&gt;&lt;code&gt;tar -xzvf a.tgz&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gzip / gunzip&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;gzip files&lt;/td&gt;
&lt;td&gt;&lt;code&gt;gzip -9 big.log&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;bzip2 / bunzip2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;bzip2&lt;/td&gt;
&lt;td&gt;&lt;code&gt;bzip2 file&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;xz / unxz&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;xz (best ratio)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;xz -T0 huge.tar&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;zip / unzip&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Zip format&lt;/td&gt;
&lt;td&gt;&lt;code&gt;unzip -d out/ a.zip&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;zcat / zless&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;View gzipped&lt;/td&gt;
&lt;td&gt;`zcat app.log.gz&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  Permissions
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;{% raw %}&lt;code&gt;chmod&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Change mode&lt;/td&gt;
&lt;td&gt;&lt;code&gt;chmod 750 deploy.sh&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;chown&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Change owner&lt;/td&gt;
&lt;td&gt;&lt;code&gt;chown -R deploy:deploy /opt/app&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;chgrp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Change group&lt;/td&gt;
&lt;td&gt;&lt;code&gt;chgrp www-data /var/www&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;umask&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Default mask&lt;/td&gt;
&lt;td&gt;&lt;code&gt;umask 027&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  Disk
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;df&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Filesystem usage&lt;/td&gt;
&lt;td&gt;&lt;code&gt;df -hT&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;du&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Directory usage&lt;/td&gt;
&lt;td&gt;`du -sh *&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;{% raw %}&lt;code&gt;lsblk&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Block devices&lt;/td&gt;
&lt;td&gt;&lt;code&gt;lsblk -f&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;fdisk&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Partition tool&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo fdisk -l&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;parted&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;GPT partitioning&lt;/td&gt;
&lt;td&gt;&lt;code&gt;parted /dev/sdb print&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mkfs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Make filesystem&lt;/td&gt;
&lt;td&gt;&lt;code&gt;mkfs.ext4 /dev/sdb1&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mount / umount&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Mount FS&lt;/td&gt;
&lt;td&gt;&lt;code&gt;mount /dev/sdb1 /mnt&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;blkid&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;UUIDs/labels&lt;/td&gt;
&lt;td&gt;&lt;code&gt;blkid /dev/sda1&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ncdu&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;TUI disk usage&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ncdu /var&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;fsck&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Filesystem check&lt;/td&gt;
&lt;td&gt;&lt;code&gt;fsck -y /dev/sda1&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;lvm tools&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;LVM&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pvs / vgs / lvs&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  Miscellaneous
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;man / info&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Manuals&lt;/td&gt;
&lt;td&gt;&lt;code&gt;man 5 crontab&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tldr&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Practical examples&lt;/td&gt;
&lt;td&gt;&lt;code&gt;tldr tar&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;history&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Shell history&lt;/td&gt;
&lt;td&gt;`history&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;{% raw %}&lt;code&gt;alias&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Define alias&lt;/td&gt;
&lt;td&gt;&lt;code&gt;alias ll='ls -lah'&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;watch&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Repeat command&lt;/td&gt;
&lt;td&gt;&lt;code&gt;watch -n 2 'df -h'&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;time&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Time a command&lt;/td&gt;
&lt;td&gt;&lt;code&gt;time ./build.sh&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;timeout&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Limit runtime&lt;/td&gt;
&lt;td&gt;&lt;code&gt;timeout 30s ./probe&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;yes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Repeat string&lt;/td&gt;
&lt;td&gt;`yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;{% raw %}&lt;code&gt;screen / tmux&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Terminal multiplexer&lt;/td&gt;
&lt;td&gt;&lt;code&gt;tmux new -s work&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;echo / printf&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print&lt;/td&gt;
&lt;td&gt;&lt;code&gt;printf "%s\n" "$VAR"&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;true / false&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Exit code helpers&lt;/td&gt;
&lt;td&gt;&lt;code&gt;while true; do ...; done&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  2. Vim Cheat Sheet
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Modes
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mode&lt;/th&gt;
&lt;th&gt;Enter&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Normal&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Esc&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Default; movement &amp;amp; commands&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Insert&lt;/td&gt;
&lt;td&gt;&lt;code&gt;i I a A o O&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Type text&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Visual&lt;/td&gt;
&lt;td&gt;&lt;code&gt;v&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Character selection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Visual line&lt;/td&gt;
&lt;td&gt;&lt;code&gt;V&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Line selection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Visual block&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Ctrl+v&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Column / rectangular edit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Command&lt;/td&gt;
&lt;td&gt;&lt;code&gt;:&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Ex commands (&lt;code&gt;:w&lt;/code&gt;, &lt;code&gt;:q&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Replace&lt;/td&gt;
&lt;td&gt;&lt;code&gt;R&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Overwrite mode&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Terminal&lt;/td&gt;
&lt;td&gt;&lt;code&gt;:terminal&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Embedded shell&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  Navigation
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Key&lt;/th&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;h j k l&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Left, down, up, right&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;w / b&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Next / previous word&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;e / ge&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;End of word / previous word end&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;0 / ^ / $&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Line start / first non-blank / line end&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gg / G&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;File top / file bottom&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;:42&lt;/code&gt; or &lt;code&gt;42G&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Jump to line 42&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Ctrl+d / Ctrl+u&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Half page down / up&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Ctrl+f / Ctrl+b&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Full page down / up&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;{ / }&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Previous / next blank line&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;%&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Match bracket&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;f x / F x&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Find char forward / backward&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;* / #&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Search word under cursor forward / backward&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  Editing
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Key&lt;/th&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;i / a&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Insert before / after cursor&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;I / A&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Insert at line start / end&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;o / O&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;New line below / above&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;x / X&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Delete char forward / backward&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dd / D&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Delete line / to end of line&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dw / d$ / dG&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Delete word / to EOL / to EOF&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;yy / Y&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Yank line&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;p / P&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Paste after / before&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cc / C&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Change line / to EOL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cw / ciw / ci"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Change word / inner word / inside quotes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;r / R&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Replace one char / replace mode&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;u / Ctrl+r&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Undo / redo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Repeat last change&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;gt;&amp;gt; / &amp;lt;&amp;lt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Indent / dedent line&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;J&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Join line below&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  Search &amp;amp; Replace
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/pattern        &lt;span class="s2"&gt;" Search forward
?pattern        "&lt;/span&gt; Search backward
n / N           &lt;span class="s2"&gt;" Next / previous match
:noh            "&lt;/span&gt; Clear highlight

:s/old/new/         &lt;span class="s2"&gt;" Replace first on line
:s/old/new/g        "&lt;/span&gt; Replace all on line
:%s/old/new/g       &lt;span class="s2"&gt;" Replace all in file
:%s/old/new/gc      "&lt;/span&gt; Confirm each
:%s/old/new/gI      &lt;span class="s2"&gt;" Case-sensitive (capital I)
:5,15s/old/new/g    "&lt;/span&gt; Replace &lt;span class="k"&gt;in &lt;/span&gt;line range
:&lt;span class="s1"&gt;'&amp;lt;,'&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;s/old/new/g    &lt;span class="s2"&gt;" Replace in visual selection
:g/pattern/d        "&lt;/span&gt; Delete all lines matching
:v/pattern/d        &lt;span class="s2"&gt;" Delete lines NOT matching
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Visual Mode
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Key&lt;/th&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;v / V / Ctrl+v&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Char / line / block selection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;d / y / c&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Delete / yank / change selection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;gt; / &amp;lt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Indent / dedent block&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;~&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Toggle case&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;u / U&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Lower / upper case selection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;:'&amp;lt;,'&amp;gt;sort&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Sort selected lines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;Ctrl+v&lt;/code&gt; then &lt;code&gt;I text Esc&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Insert column prefix on all lines&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  Advanced — Registers, Marks, Macros, Splits, Tabs
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="s2"&gt;" Registers (named clipboards)
"&lt;/span&gt;ayy            &lt;span class="s2"&gt;" Yank line to register a
"&lt;/span&gt;ap            &lt;span class="s2"&gt;" Paste register a
:reg            "&lt;/span&gt; Show all registers
&lt;span class="s2"&gt;"+y             "&lt;/span&gt; Yank to system clipboard &lt;span class="o"&gt;(&lt;/span&gt;needs +clipboard&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="s2"&gt;"+p             "&lt;/span&gt; Paste from system clipboard

&lt;span class="s2"&gt;" Marks
ma              "&lt;/span&gt; Set mark a at cursor
&lt;span class="s1"&gt;'a              " Jump to line of mark a
`a              " Jump to exact position of mark a
:marks          " List all marks

" Macros
qa              " Start recording macro into a
...do stuff...
q               " Stop recording
@a              " Play macro a
@@              " Replay last macro
10@a            " Play macro 10 times

" Splits
:split file     " Horizontal split  (or :sp)
:vsplit file    " Vertical split    (or :vsp)
Ctrl+w h/j/k/l  " Move between splits
Ctrl+w =        " Equalize sizes
Ctrl+w _        " Maximize height
Ctrl+w |        " Maximize width
Ctrl+w c        " Close split

" Tabs
:tabnew file    " Open in new tab
gt / gT         " Next / previous tab
:tabclose       " Close current tab
:tabs           " List tabs
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. File Permissions Reference
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Octal Table (000–777)
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Octal&lt;/th&gt;
&lt;th&gt;Binary&lt;/th&gt;
&lt;th&gt;rwx&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;000&lt;/td&gt;
&lt;td&gt;&lt;code&gt;---&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No access&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;001&lt;/td&gt;
&lt;td&gt;&lt;code&gt;--x&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Execute only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;010&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-w-&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Write only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;011&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-wx&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Write + execute&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;&lt;code&gt;r--&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Read only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;td&gt;&lt;code&gt;r-x&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Read + execute&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;110&lt;/td&gt;
&lt;td&gt;&lt;code&gt;rw-&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Read + write&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;111&lt;/td&gt;
&lt;td&gt;&lt;code&gt;rwx&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Full access&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Three digits = owner, group, others. So &lt;code&gt;750&lt;/code&gt; = &lt;code&gt;rwxr-x---&lt;/code&gt; (owner full, group r-x, others nothing).&lt;/p&gt;

&lt;h4&gt;
  
  
  Special Bits
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Bit&lt;/th&gt;
&lt;th&gt;Octal&lt;/th&gt;
&lt;th&gt;Symbol&lt;/th&gt;
&lt;th&gt;Effect&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;setuid&lt;/td&gt;
&lt;td&gt;4000&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;s&lt;/code&gt; in owner-x&lt;/td&gt;
&lt;td&gt;Run binary as file owner (e.g. &lt;code&gt;passwd&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;setgid&lt;/td&gt;
&lt;td&gt;2000&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;s&lt;/code&gt; in group-x&lt;/td&gt;
&lt;td&gt;On dir: new files inherit group&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;sticky&lt;/td&gt;
&lt;td&gt;1000&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;t&lt;/code&gt; in others-x&lt;/td&gt;
&lt;td&gt;Only owner can delete (e.g. &lt;code&gt;/tmp&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chmod &lt;/span&gt;4755 /usr/local/bin/myapp      &lt;span class="c"&gt;# setuid + 755&lt;/span&gt;
&lt;span class="nb"&gt;chmod &lt;/span&gt;2775 /shared/uploads           &lt;span class="c"&gt;# setgid + 775&lt;/span&gt;
&lt;span class="nb"&gt;chmod &lt;/span&gt;1777 /tmp                      &lt;span class="c"&gt;# sticky + 777&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Common Permission Patterns
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mode&lt;/th&gt;
&lt;th&gt;Use Case&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;644&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Regular file (configs, code)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;600&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Sensitive file (SSH private key, secrets)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;640&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Owner rw, group read (shared config)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;755&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Executable script, directory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;750&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Private executable / private dir&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;700&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Only owner (e.g. &lt;code&gt;~/.ssh&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;444&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Read-only for everyone&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;777&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Almost always wrong — security smell&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  chmod Symbolic Operators
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Who&lt;/th&gt;
&lt;th&gt;Op&lt;/th&gt;
&lt;th&gt;Perm&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;u&lt;/code&gt; user&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;+&lt;/code&gt; add&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;r&lt;/code&gt; read&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;g&lt;/code&gt; group&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;-&lt;/code&gt; remove&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;w&lt;/code&gt; write&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;o&lt;/code&gt; others&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;=&lt;/code&gt; set&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;x&lt;/code&gt; execute&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;a&lt;/code&gt; all&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;X&lt;/code&gt; exec only on dirs/already-exec files&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chmod &lt;/span&gt;u+x deploy.sh             &lt;span class="c"&gt;# add execute for owner&lt;/span&gt;
&lt;span class="nb"&gt;chmod &lt;/span&gt;go-rwx secret.key         &lt;span class="c"&gt;# remove all from group + others&lt;/span&gt;
&lt;span class="nb"&gt;chmod &lt;/span&gt;&lt;span class="nv"&gt;a&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;r README.md             &lt;span class="c"&gt;# read-only for all&lt;/span&gt;
&lt;span class="nb"&gt;chmod&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; &lt;span class="nv"&gt;u&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;rwX,g&lt;span class="o"&gt;=&lt;/span&gt;rX,o&lt;span class="o"&gt;=&lt;/span&gt; /app     &lt;span class="c"&gt;# safe recursive (capital X)&lt;/span&gt;
&lt;span class="nb"&gt;chmod &lt;/span&gt;u+s /usr/bin/myapp        &lt;span class="c"&gt;# setuid&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  chown / chgrp
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chown &lt;/span&gt;alice file                 &lt;span class="c"&gt;# change owner&lt;/span&gt;
&lt;span class="nb"&gt;chown &lt;/span&gt;alice:devs file            &lt;span class="c"&gt;# owner + group&lt;/span&gt;
&lt;span class="nb"&gt;chown&lt;/span&gt; :devs file                 &lt;span class="c"&gt;# only group&lt;/span&gt;
&lt;span class="nb"&gt;chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; deploy:deploy /opt/app  &lt;span class="c"&gt;# recursive&lt;/span&gt;
&lt;span class="nb"&gt;chown&lt;/span&gt; &lt;span class="nt"&gt;--reference&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;other.txt file &lt;span class="c"&gt;# copy ownership from other.txt&lt;/span&gt;
&lt;span class="nb"&gt;chgrp &lt;/span&gt;www-data /var/www          &lt;span class="c"&gt;# change group only&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Bash Scripting Cheat Sheet
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Variables &amp;amp; Parameters
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"alice"&lt;/span&gt;           &lt;span class="c"&gt;# no spaces around =&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$name&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;           &lt;span class="c"&gt;# always quote variable expansions&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_log"&lt;/span&gt;     &lt;span class="c"&gt;# disambiguate with braces&lt;/span&gt;
&lt;span class="nb"&gt;readonly &lt;/span&gt;&lt;span class="nv"&gt;PI&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3.14       &lt;span class="c"&gt;# constant&lt;/span&gt;
&lt;span class="nb"&gt;unset &lt;/span&gt;name             &lt;span class="c"&gt;# delete variable&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PATH&lt;/span&gt;&lt;span class="s2"&gt;:/opt/bin"&lt;/span&gt;   &lt;span class="c"&gt;# export to children&lt;/span&gt;

&lt;span class="c"&gt;# Positional params inside a script&lt;/span&gt;
&lt;span class="nv"&gt;$0&lt;/span&gt;       &lt;span class="c"&gt;# script name&lt;/span&gt;
&lt;span class="nv"&gt;$1&lt;/span&gt;..&lt;span class="nv"&gt;$9&lt;/span&gt;   &lt;span class="c"&gt;# args 1..9&lt;/span&gt;
&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;10&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;    &lt;span class="c"&gt;# 10th arg (need braces)&lt;/span&gt;
&lt;span class="nv"&gt;$# &lt;/span&gt;      &lt;span class="c"&gt;# number of args&lt;/span&gt;
&lt;span class="nv"&gt;$@&lt;/span&gt;       &lt;span class="c"&gt;# all args (each quoted separately when "$@")&lt;/span&gt;
&lt;span class="nv"&gt;$*&lt;/span&gt;       &lt;span class="c"&gt;# all args (one string when "$*")&lt;/span&gt;
&lt;span class="nv"&gt;$?&lt;/span&gt;       &lt;span class="c"&gt;# exit code of last command&lt;/span&gt;
&lt;span class="nv"&gt;$$&lt;/span&gt;       &lt;span class="c"&gt;# current PID&lt;/span&gt;
&lt;span class="nv"&gt;$!&lt;/span&gt;       &lt;span class="c"&gt;# PID of last backgrounded command&lt;/span&gt;

&lt;span class="c"&gt;# Parameter expansion&lt;/span&gt;
&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;var&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;default&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;    &lt;span class="c"&gt;# use default if var unset/empty&lt;/span&gt;
&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;var&lt;/span&gt;:&lt;span class="p"&gt;=default&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;    &lt;span class="c"&gt;# also assign default&lt;/span&gt;
&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;var&lt;/span&gt;:?error&lt;span class="k"&gt;}&lt;/span&gt;      &lt;span class="c"&gt;# error if unset&lt;/span&gt;
&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;var&lt;/span&gt;:+alt&lt;span class="k"&gt;}&lt;/span&gt;        &lt;span class="c"&gt;# use alt if var IS set&lt;/span&gt;
&lt;span class="k"&gt;${#&lt;/span&gt;&lt;span class="nv"&gt;var&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;            &lt;span class="c"&gt;# string length&lt;/span&gt;
&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;var&lt;/span&gt;:2:4&lt;span class="k"&gt;}&lt;/span&gt;         &lt;span class="c"&gt;# substring from pos 2, length 4&lt;/span&gt;
&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;var&lt;/span&gt;&lt;span class="p"&gt;/foo/bar&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;     &lt;span class="c"&gt;# replace first foo with bar&lt;/span&gt;
&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;var&lt;/span&gt;&lt;span class="p"&gt;//foo/bar&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;    &lt;span class="c"&gt;# replace ALL foo&lt;/span&gt;
&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;var&lt;/span&gt;&lt;span class="p"&gt;#prefix&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;      &lt;span class="c"&gt;# strip shortest prefix&lt;/span&gt;
&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;var&lt;/span&gt;&lt;span class="p"&gt;##prefix&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;     &lt;span class="c"&gt;# strip longest prefix&lt;/span&gt;
&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;var&lt;/span&gt;&lt;span class="p"&gt;%.txt&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;        &lt;span class="c"&gt;# strip shortest suffix&lt;/span&gt;
&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;var&lt;/span&gt;&lt;span class="p"&gt;%%.*&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;         &lt;span class="c"&gt;# strip longest suffix&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Test Operators
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Operator&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Integer&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-eq&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;equal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Integer&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-ne&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;not equal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Integer&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-lt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;less than&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Integer&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-le&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;less or equal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Integer&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-gt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;greater than&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Integer&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-ge&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;greater or equal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;String&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;=&lt;/code&gt; / &lt;code&gt;==&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;equal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;String&lt;/td&gt;
&lt;td&gt;&lt;code&gt;!=&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;not equal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;String&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-z&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;zero-length (empty)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;String&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-n&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;non-empty&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;String&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;lt; / &amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;lexicographic (inside &lt;code&gt;[[ ]]&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;File&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-e&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;exists&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;File&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-f&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;is regular file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;File&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-d&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;is directory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;File&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-L&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;is symlink&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;File&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-r / -w / -x&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;readable / writable / executable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;File&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-s&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;non-empty (size &amp;gt; 0)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;File&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-O&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;owned by you&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;File&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-G&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;owned by your group&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;File&lt;/td&gt;
&lt;td&gt;&lt;code&gt;a -nt b&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;a newer than b&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;File&lt;/td&gt;
&lt;td&gt;&lt;code&gt;a -ot b&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;a older than b&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  Control Structures
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# if / elif / else&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nv"&gt;$count&lt;/span&gt; &lt;span class="nt"&gt;-gt&lt;/span&gt; 10 &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"many"&lt;/span&gt;
&lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$name&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"no name"&lt;/span&gt;
&lt;span class="k"&gt;else
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"ok"&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# case&lt;/span&gt;
&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$env&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in
  &lt;/span&gt;prod|production&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"prod"&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
  dev|development&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"dev"&lt;/span&gt;  &lt;span class="p"&gt;;;&lt;/span&gt;
  &lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;                &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"unknown"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;1 &lt;span class="p"&gt;;;&lt;/span&gt;
&lt;span class="k"&gt;esac&lt;/span&gt;

&lt;span class="c"&gt;# for&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;f &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;.log&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;&lt;span class="nb"&gt;gzip&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;done
for &lt;/span&gt;i &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;1..5&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;done
for&lt;/span&gt; &lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0&lt;span class="p"&gt;;&lt;/span&gt; i&amp;lt;10&lt;span class="p"&gt;;&lt;/span&gt; i++&lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;done&lt;/span&gt;

&lt;span class="c"&gt;# while / until&lt;/span&gt;
&lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nb"&gt;read&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; line&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"got: &lt;/span&gt;&lt;span class="nv"&gt;$line&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt; &amp;lt; file.txt

&lt;span class="nv"&gt;count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0
&lt;span class="k"&gt;until&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nv"&gt;$count&lt;/span&gt; &lt;span class="nt"&gt;-ge&lt;/span&gt; 5 &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="o"&gt;((&lt;/span&gt;count++&lt;span class="o"&gt;))&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;

&lt;span class="c"&gt;# functions&lt;/span&gt;
deploy&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;local env&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;      &lt;span class="c"&gt;# 'local' avoids polluting global scope&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"deploying to &lt;/span&gt;&lt;span class="nv"&gt;$env&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;0
&lt;span class="o"&gt;}&lt;/span&gt;
deploy prod
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Common Idioms
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Strict mode (put at top of every serious script)&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-euo&lt;/span&gt; pipefail
&lt;span class="nv"&gt;IFS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;$'&lt;/span&gt;&lt;span class="se"&gt;\n\t&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;

&lt;span class="c"&gt;# Check command exists&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; jq &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null 2&amp;gt;&amp;amp;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"jq required"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# Retry loop with backoff&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;attempt &lt;span class="k"&gt;in &lt;/span&gt;1 2 3 4 5&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$URL&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;break
  sleep&lt;/span&gt; &lt;span class="k"&gt;$((&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; attempt&lt;span class="k"&gt;))&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;

&lt;span class="c"&gt;# Parse args (getopts)&lt;/span&gt;
&lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nb"&gt;getopts&lt;/span&gt; &lt;span class="s2"&gt;"e:vh"&lt;/span&gt; opt&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  case&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$opt&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in
    &lt;/span&gt;e&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;ENV&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$OPTARG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
    v&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;VERBOSE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="p"&gt;;;&lt;/span&gt;
    h&lt;span class="p"&gt;)&lt;/span&gt; usage&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;0 &lt;span class="p"&gt;;;&lt;/span&gt;
    &lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; usage&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;1 &lt;span class="p"&gt;;;&lt;/span&gt;
  &lt;span class="k"&gt;esac&lt;/span&gt;
&lt;span class="k"&gt;done
&lt;/span&gt;&lt;span class="nb"&gt;shift&lt;/span&gt; &lt;span class="k"&gt;$((&lt;/span&gt;OPTIND &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt;

&lt;span class="c"&gt;# Trap cleanup&lt;/span&gt;
&lt;span class="nv"&gt;tmp&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;mktemp&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;trap&lt;/span&gt; &lt;span class="s1"&gt;'rm -rf "$tmp"'&lt;/span&gt; EXIT INT TERM

&lt;span class="c"&gt;# Read file line by line (safe)&lt;/span&gt;
&lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nv"&gt;IFS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;read&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; line &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$line&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;process &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$line&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt; &amp;lt; input.txt

&lt;span class="c"&gt;# Heredoc&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt; &amp;gt; /etc/myapp/config
host=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;hostname&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;
env=&lt;/span&gt;&lt;span class="nv"&gt;$ENV&lt;/span&gt;&lt;span class="sh"&gt;
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Arrays
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Indexed array&lt;/span&gt;
&lt;span class="nv"&gt;servers&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;web1 web2 web3&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;servers&lt;/span&gt;&lt;span class="p"&gt;[0]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;          &lt;span class="c"&gt;# web1&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;servers&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;          &lt;span class="c"&gt;# all elements&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${#&lt;/span&gt;&lt;span class="nv"&gt;servers&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;         &lt;span class="c"&gt;# length = 3&lt;/span&gt;
servers+&lt;span class="o"&gt;=(&lt;/span&gt;web4&lt;span class="o"&gt;)&lt;/span&gt;               &lt;span class="c"&gt;# append&lt;/span&gt;
&lt;span class="nb"&gt;unset&lt;/span&gt; &lt;span class="s1"&gt;'servers[1]'&lt;/span&gt;            &lt;span class="c"&gt;# delete element&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;s &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;servers&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$s&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;done&lt;/span&gt;

&lt;span class="c"&gt;# Associative array (bash 4+)&lt;/span&gt;
&lt;span class="nb"&gt;declare&lt;/span&gt; &lt;span class="nt"&gt;-A&lt;/span&gt; user_role
user_role[alice]&lt;span class="o"&gt;=&lt;/span&gt;admin
user_role[bob]&lt;span class="o"&gt;=&lt;/span&gt;dev
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;user_role&lt;/span&gt;&lt;span class="p"&gt;[alice]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;k &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="p"&gt;!user_role[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$k&lt;/span&gt;&lt;span class="s2"&gt; =&amp;gt; &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;user_role&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$k&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Brace Expansion
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo &lt;/span&gt;file&lt;span class="o"&gt;{&lt;/span&gt;1,2,3&lt;span class="o"&gt;}&lt;/span&gt;.txt          &lt;span class="c"&gt;# file1.txt file2.txt file3.txt&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;a..e&lt;span class="o"&gt;}&lt;/span&gt;                   &lt;span class="c"&gt;# a b c d e&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;01..05&lt;span class="o"&gt;}&lt;/span&gt;                 &lt;span class="c"&gt;# 01 02 03 04 05&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;1..10..2&lt;span class="o"&gt;}&lt;/span&gt;               &lt;span class="c"&gt;# 1 3 5 7 9&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; project/&lt;span class="o"&gt;{&lt;/span&gt;src,test,docs&lt;span class="o"&gt;}&lt;/span&gt;/&lt;span class="o"&gt;{&lt;/span&gt;api,web&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="nb"&gt;cp &lt;/span&gt;app.conf&lt;span class="o"&gt;{&lt;/span&gt;,.bak&lt;span class="o"&gt;}&lt;/span&gt;            &lt;span class="c"&gt;# quick backup -&amp;gt; app.conf, app.conf.bak&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Regular Expressions
&lt;/h3&gt;

&lt;h4&gt;
  
  
  BRE vs ERE Side-by-Side
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;BRE (grep, sed)&lt;/th&gt;
&lt;th&gt;ERE (egrep, grep -E, sed -E, awk)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Alternation&lt;/td&gt;
&lt;td&gt;`\&lt;/td&gt;
&lt;td&gt;`&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Grouping&lt;/td&gt;
&lt;td&gt;&lt;code&gt;\( \)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;( )&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;One or more&lt;/td&gt;
&lt;td&gt;&lt;code&gt;\+&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;+&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Zero or one&lt;/td&gt;
&lt;td&gt;&lt;code&gt;\?&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;?&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Repetition&lt;/td&gt;
&lt;td&gt;&lt;code&gt;\{m,n\}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{m,n}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Zero or more&lt;/td&gt;
&lt;td&gt;&lt;code&gt;*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;*&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Backrefs&lt;/td&gt;
&lt;td&gt;&lt;code&gt;\1 \2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;\1 \2&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Rule of thumb: use &lt;code&gt;grep -E&lt;/code&gt; (ERE) — fewer backslashes, fewer tears.&lt;/p&gt;

&lt;h4&gt;
  
  
  Character Classes
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Class&lt;/th&gt;
&lt;th&gt;Matches&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Any single char (except newline)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;[abc]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;a, b, or c&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;[^abc]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;NOT a, b, c&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;[a-z]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Lowercase letters&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;[A-Za-z0-9_]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;"word" chars&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;\d&lt;/code&gt; / &lt;code&gt;\D&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Digit / non-digit (PCRE)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;\w&lt;/code&gt; / &lt;code&gt;\W&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Word / non-word (PCRE)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;\s&lt;/code&gt; / &lt;code&gt;\S&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Whitespace / non-ws (PCRE)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;[[:alpha:]]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;POSIX alpha&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;[[:digit:]]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;POSIX digit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;[[:space:]]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;POSIX whitespace&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;[[:alnum:]]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;POSIX alphanumeric&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;[[:upper:]]&lt;/code&gt; / &lt;code&gt;[[:lower:]]&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;POSIX case&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;[[:punct:]]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;POSIX punctuation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  Anchors &amp;amp; Quantifiers
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pattern&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;^&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Start of line&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;End of line&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\b&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Word boundary&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\B&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Non-word-boundary&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;\A&lt;/code&gt; / &lt;code&gt;\Z&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Start / end of input (PCRE)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;0 or more&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;+&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1 or more&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;?&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;0 or 1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;{3}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Exactly 3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;{3,}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;3 or more&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;{3,5}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Between 3 and 5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;*?&lt;/code&gt; &lt;code&gt;+?&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Lazy / non-greedy (PCRE)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  10 Example Patterns
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Matches&lt;/th&gt;
&lt;th&gt;Pattern (ERE)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Email (pragmatic)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;IPv4 address (loose)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;([0-9]{1,3}\.){3}[0-9]{1,3}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;IPv4 strict (0–255)&lt;/td&gt;
&lt;td&gt;`((25[0-5]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;URL (http/https)&lt;/td&gt;
&lt;td&gt;{% raw %}&lt;code&gt;https?://[A-Za-z0-9._~:/?#\[\]@!$&amp;amp;'()*+,;=%-]+&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Date YYYY-MM-DD&lt;/td&gt;
&lt;td&gt;`[0-9]{4}-(0[1-9]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Time HH:MM:SS (24h)&lt;/td&gt;
&lt;td&gt;{% raw %}`([01][0-9]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;ISO timestamp&lt;/td&gt;
&lt;td&gt;{% raw %}`[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(.[0-9]+)?(Z&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;E.164 phone number&lt;/td&gt;
&lt;td&gt;{% raw %}&lt;code&gt;\+[1-9][0-9]{7,14}&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;UUIDv4&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;Nginx access log IP&lt;/td&gt;
&lt;td&gt;&lt;code&gt;^([0-9]{1,3}\.){3}[0-9]{1,3}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Practical usage&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s1"&gt;'([0-9]{1,3}\.){3}[0-9]{1,3}'&lt;/span&gt; access.log
&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s1"&gt;'s/[0-9]{4}-[0-9]{2}-[0-9]{2}/REDACTED/g'&lt;/span&gt; file
&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'/ERROR|FATAL/ {print}'&lt;/span&gt; app.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  6. SSH Commands Cheat Sheet
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Key Generation &amp;amp; Distribution
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Generate modern keypair (ed25519 preferred)&lt;/span&gt;
ssh-keygen &lt;span class="nt"&gt;-t&lt;/span&gt; ed25519 &lt;span class="nt"&gt;-C&lt;/span&gt; &lt;span class="s2"&gt;"pushkar@1buy.ai"&lt;/span&gt;
ssh-keygen &lt;span class="nt"&gt;-t&lt;/span&gt; rsa &lt;span class="nt"&gt;-b&lt;/span&gt; 4096 &lt;span class="nt"&gt;-C&lt;/span&gt; &lt;span class="s2"&gt;"fallback"&lt;/span&gt;     &lt;span class="c"&gt;# legacy systems&lt;/span&gt;

&lt;span class="c"&gt;# View public key&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; ~/.ssh/id_ed25519.pub

&lt;span class="c"&gt;# Copy public key to server (easiest)&lt;/span&gt;
ssh-copy-id &lt;span class="nt"&gt;-i&lt;/span&gt; ~/.ssh/id_ed25519.pub deploy@10.0.0.5

&lt;span class="c"&gt;# Manually&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; ~/.ssh/id_ed25519.pub | ssh deploy@host &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s1"&gt;'mkdir -p ~/.ssh &amp;amp;&amp;amp; chmod 700 ~/.ssh &amp;amp;&amp;amp; \
   cat &amp;gt;&amp;gt; ~/.ssh/authorized_keys &amp;amp;&amp;amp; chmod 600 ~/.ssh/authorized_keys'&lt;/span&gt;

&lt;span class="c"&gt;# Fingerprint&lt;/span&gt;
ssh-keygen &lt;span class="nt"&gt;-lf&lt;/span&gt; ~/.ssh/id_ed25519.pub
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  ~/.ssh/config Snippets
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Host &lt;span class="k"&gt;*&lt;/span&gt;
  ServerAliveInterval 60
  ServerAliveCountMax 3
  HashKnownHosts &lt;span class="nb"&gt;yes

&lt;/span&gt;Host prod-web
  HostName 10.0.0.5
  User deploy
  Port 2222
  IdentityFile ~/.ssh/id_ed25519
  IdentitiesOnly &lt;span class="nb"&gt;yes

&lt;/span&gt;Host bastion
  HostName bastion.1buy.ai
  User pushkar

&lt;span class="c"&gt;# Jump through bastion to private host&lt;/span&gt;
Host db-private
  HostName 10.0.5.20
  User dbadmin
  ProxyJump bastion

&lt;span class="c"&gt;# Now just: ssh prod-web   /   ssh db-private&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Tunneling (L / R / D)
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Flag&lt;/th&gt;
&lt;th&gt;Direction&lt;/th&gt;
&lt;th&gt;Use Case&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-L&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Local → Remote&lt;/td&gt;
&lt;td&gt;Access remote service via local port&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-R&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remote → Local&lt;/td&gt;
&lt;td&gt;Expose local service on remote host&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-D&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Dynamic (SOCKS)&lt;/td&gt;
&lt;td&gt;Use server as SOCKS5 proxy&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Local forward: localhost:5433 -&amp;gt; db.internal:5432 via bastion&lt;/span&gt;
ssh &lt;span class="nt"&gt;-L&lt;/span&gt; 5433:db.internal:5432 bastion
psql &lt;span class="nt"&gt;-h&lt;/span&gt; localhost &lt;span class="nt"&gt;-p&lt;/span&gt; 5433 &lt;span class="nt"&gt;-U&lt;/span&gt; app

&lt;span class="c"&gt;# Remote forward: expose local dev server on remote:9000&lt;/span&gt;
ssh &lt;span class="nt"&gt;-R&lt;/span&gt; 9000:localhost:3000 user@remote

&lt;span class="c"&gt;# Dynamic / SOCKS proxy on localhost:1080&lt;/span&gt;
ssh &lt;span class="nt"&gt;-D&lt;/span&gt; 1080 &lt;span class="nt"&gt;-C&lt;/span&gt; &lt;span class="nt"&gt;-N&lt;/span&gt; bastion
&lt;span class="c"&gt;# Configure browser SOCKS5 -&amp;gt; localhost:1080&lt;/span&gt;

&lt;span class="c"&gt;# Run tunnel in background, no shell&lt;/span&gt;
ssh &lt;span class="nt"&gt;-fN&lt;/span&gt; &lt;span class="nt"&gt;-L&lt;/span&gt; 5433:db:5432 bastion
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  SSH Agent
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;eval&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;ssh-agent &lt;span class="nt"&gt;-s&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;          &lt;span class="c"&gt;# start agent&lt;/span&gt;
ssh-add ~/.ssh/id_ed25519       &lt;span class="c"&gt;# load key&lt;/span&gt;
ssh-add &lt;span class="nt"&gt;-l&lt;/span&gt;                      &lt;span class="c"&gt;# list loaded keys&lt;/span&gt;
ssh-add &lt;span class="nt"&gt;-D&lt;/span&gt;                      &lt;span class="c"&gt;# remove all&lt;/span&gt;
ssh &lt;span class="nt"&gt;-A&lt;/span&gt; user@host                &lt;span class="c"&gt;# forward agent (use cautiously)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  scp / sftp / rsync
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# scp (simple)&lt;/span&gt;
scp file.txt user@host:/tmp/
scp &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="nb"&gt;dir&lt;/span&gt;/ user@host:/opt/
scp &lt;span class="nt"&gt;-P&lt;/span&gt; 2222 &lt;span class="nt"&gt;-i&lt;/span&gt; key.pem ...      &lt;span class="c"&gt;# custom port, custom key&lt;/span&gt;
scp user@host:/etc/nginx.conf &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="c"&gt;# sftp (interactive)&lt;/span&gt;
sftp user@host
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; put localfile
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; get remotefile
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; bye

&lt;span class="c"&gt;# rsync (best for repeated syncs)&lt;/span&gt;
rsync &lt;span class="nt"&gt;-avz&lt;/span&gt; &lt;span class="nt"&gt;--progress&lt;/span&gt; src/ user@host:/dst/
rsync &lt;span class="nt"&gt;-avz&lt;/span&gt; &lt;span class="nt"&gt;--delete&lt;/span&gt; src/ user@host:/dst/        &lt;span class="c"&gt;# mirror (deletes extras)&lt;/span&gt;
rsync &lt;span class="nt"&gt;-avz&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"ssh -p 2222 -i key.pem"&lt;/span&gt; src/ host:/dst/
rsync &lt;span class="nt"&gt;-avz&lt;/span&gt; &lt;span class="nt"&gt;--exclude&lt;/span&gt; &lt;span class="s1"&gt;'.git'&lt;/span&gt; &lt;span class="nt"&gt;--exclude&lt;/span&gt; &lt;span class="s1"&gt;'node_modules'&lt;/span&gt; src/ host:/dst/
rsync &lt;span class="nt"&gt;-avzn&lt;/span&gt; src/ host:/dst/                     &lt;span class="c"&gt;# -n = dry run&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  sshd_config Hardening (server side)
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# /etc/ssh/sshd_config&lt;/span&gt;
Port 2222                          &lt;span class="c"&gt;# change default&lt;/span&gt;
PermitRootLogin no                 &lt;span class="c"&gt;# never allow root login&lt;/span&gt;
PasswordAuthentication no          &lt;span class="c"&gt;# keys only&lt;/span&gt;
PubkeyAuthentication &lt;span class="nb"&gt;yes
&lt;/span&gt;PermitEmptyPasswords no
ChallengeResponseAuthentication no
UsePAM &lt;span class="nb"&gt;yes
&lt;/span&gt;X11Forwarding no
MaxAuthTries 3
LoginGraceTime 30
ClientAliveInterval 300
ClientAliveCountMax 2
AllowUsers deploy pushkar
AllowGroups sshusers
Protocol 2
KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com

&lt;span class="c"&gt;# Apply&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;sshd &lt;span class="nt"&gt;-t&lt;/span&gt;                       &lt;span class="c"&gt;# test config first!&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl reload sshd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  7. System Monitoring
&lt;/h3&gt;

&lt;h4&gt;
  
  
  top
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;top                           &lt;span class="c"&gt;# launch&lt;/span&gt;
&lt;span class="c"&gt;# Interactive keys:&lt;/span&gt;
P    &lt;span class="nb"&gt;sort &lt;/span&gt;by CPU
M    &lt;span class="nb"&gt;sort &lt;/span&gt;by memory
T    &lt;span class="nb"&gt;sort &lt;/span&gt;by &lt;span class="nb"&gt;time
&lt;/span&gt;N    &lt;span class="nb"&gt;sort &lt;/span&gt;by PID
k    &lt;span class="nb"&gt;kill &lt;/span&gt;process &lt;span class="o"&gt;(&lt;/span&gt;asks PID + signal&lt;span class="o"&gt;)&lt;/span&gt;
r    renice
c    toggle full &lt;span class="nb"&gt;command &lt;/span&gt;line
1    toggle per-CPU view
H    toggle threads
W    save config to ~/.toprc
q    quit
top &lt;span class="nt"&gt;-b&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; 1                   &lt;span class="c"&gt;# batch mode (for scripts)&lt;/span&gt;
top &lt;span class="nt"&gt;-p&lt;/span&gt; 1234,5678              &lt;span class="c"&gt;# only specific PIDs&lt;/span&gt;
top &lt;span class="nt"&gt;-u&lt;/span&gt; deploy                 &lt;span class="c"&gt;# only user deploy&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  htop
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;htop                          &lt;span class="c"&gt;# nicer top&lt;/span&gt;
&lt;span class="c"&gt;# F1 help  F2 setup  F3 search  F4 filter  F5 tree&lt;/span&gt;
&lt;span class="c"&gt;# F6 sort  F7/F8 nice +/-  F9 kill  F10 quit&lt;/span&gt;
&lt;span class="c"&gt;# Space = tag a process; u = filter by user&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  ps
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ps aux                              &lt;span class="c"&gt;# BSD style, all processes&lt;/span&gt;
ps &lt;span class="nt"&gt;-ef&lt;/span&gt;                              &lt;span class="c"&gt;# System V style&lt;/span&gt;
ps &lt;span class="nt"&gt;-eLf&lt;/span&gt;                             &lt;span class="c"&gt;# threads too&lt;/span&gt;
ps &lt;span class="nt"&gt;-eo&lt;/span&gt; pid,user,%cpu,%mem,cmd &lt;span class="nt"&gt;--sort&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;-%cpu | &lt;span class="nb"&gt;head
&lt;/span&gt;ps &lt;span class="nt"&gt;--ppid&lt;/span&gt; 1234                      &lt;span class="c"&gt;# children of PID 1234&lt;/span&gt;
ps &lt;span class="nt"&gt;-C&lt;/span&gt; nginx                         &lt;span class="c"&gt;# find by command name&lt;/span&gt;
pstree &lt;span class="nt"&gt;-p&lt;/span&gt;                           &lt;span class="c"&gt;# process tree with PIDs&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Memory &amp;amp; CPU
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;free &lt;span class="nt"&gt;-h&lt;/span&gt;                             &lt;span class="c"&gt;# memory in human units&lt;/span&gt;
free &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; 2                        &lt;span class="c"&gt;# refresh every 2s&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; /proc/meminfo
&lt;span class="nb"&gt;cat&lt;/span&gt; /proc/cpuinfo

vmstat 1 5                          &lt;span class="c"&gt;# 5 samples, 1 sec apart&lt;/span&gt;
&lt;span class="c"&gt;# columns: r b | swpd free buff cache | si so | bi bo | in cs | us sy id wa st&lt;/span&gt;

iostat &lt;span class="nt"&gt;-xz&lt;/span&gt; 1                        &lt;span class="c"&gt;# extended IO stats, skip idle&lt;/span&gt;
iostat &lt;span class="nt"&gt;-mx&lt;/span&gt; 2 5                      &lt;span class="c"&gt;# MB/s, 5 samples&lt;/span&gt;

mpstat &lt;span class="nt"&gt;-P&lt;/span&gt; ALL 1 3                   &lt;span class="c"&gt;# per-CPU stats&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  sar (historical perf)
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sar &lt;span class="nt"&gt;-u&lt;/span&gt; 1 5                          &lt;span class="c"&gt;# CPU&lt;/span&gt;
sar &lt;span class="nt"&gt;-r&lt;/span&gt; 1 5                          &lt;span class="c"&gt;# memory&lt;/span&gt;
sar &lt;span class="nt"&gt;-b&lt;/span&gt; 1 5                          &lt;span class="c"&gt;# IO&lt;/span&gt;
sar &lt;span class="nt"&gt;-n&lt;/span&gt; DEV 1 5                      &lt;span class="c"&gt;# network&lt;/span&gt;
sar &lt;span class="nt"&gt;-q&lt;/span&gt; 1 5                          &lt;span class="c"&gt;# load avg + run queue&lt;/span&gt;
sar &lt;span class="nt"&gt;-f&lt;/span&gt; /var/log/sysstat/sa15        &lt;span class="c"&gt;# read past data (day 15)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Kernel &amp;amp; Hardware
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dmesg &lt;span class="nt"&gt;-T&lt;/span&gt; | &lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-50&lt;/span&gt;                 &lt;span class="c"&gt;# kernel messages w/ timestamps&lt;/span&gt;
dmesg &lt;span class="nt"&gt;-w&lt;/span&gt;                            &lt;span class="c"&gt;# follow&lt;/span&gt;
lscpu                               &lt;span class="c"&gt;# CPU info&lt;/span&gt;
lsblk &lt;span class="nt"&gt;-f&lt;/span&gt;                            &lt;span class="c"&gt;# block devs + filesystems&lt;/span&gt;
lspci                               &lt;span class="c"&gt;# PCI devices&lt;/span&gt;
lsusb                               &lt;span class="c"&gt;# USB devices&lt;/span&gt;
lsmod                               &lt;span class="c"&gt;# loaded modules&lt;/span&gt;
&lt;span class="nb"&gt;uname&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt;                            &lt;span class="c"&gt;# kernel version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Disk &amp;amp; Sockets
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;df&lt;/span&gt; &lt;span class="nt"&gt;-hT&lt;/span&gt;                              &lt;span class="c"&gt;# disk usage by FS&lt;/span&gt;
&lt;span class="nb"&gt;df&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt;                               &lt;span class="c"&gt;# inode usage&lt;/span&gt;
&lt;span class="nb"&gt;du&lt;/span&gt; &lt;span class="nt"&gt;-sh&lt;/span&gt; /var/log/&lt;span class="k"&gt;*&lt;/span&gt;                   &lt;span class="c"&gt;# size per item&lt;/span&gt;
&lt;span class="nb"&gt;du&lt;/span&gt; &lt;span class="nt"&gt;-sh&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; 2&amp;gt;/dev/null | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-h&lt;/span&gt;      &lt;span class="c"&gt;# sorted&lt;/span&gt;
ncdu /                              &lt;span class="c"&gt;# interactive TUI&lt;/span&gt;

ss &lt;span class="nt"&gt;-tulnp&lt;/span&gt;                           &lt;span class="c"&gt;# listening TCP/UDP sockets + processes&lt;/span&gt;
ss &lt;span class="nt"&gt;-s&lt;/span&gt;                               &lt;span class="c"&gt;# socket summary&lt;/span&gt;
ss &lt;span class="nt"&gt;-tan&lt;/span&gt; state established           &lt;span class="c"&gt;# established TCP conns&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  8. Network Commands
&lt;/h3&gt;

&lt;h4&gt;
  
  
  ip (the modern tool)
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ip a                          &lt;span class="c"&gt;# show all addresses&lt;/span&gt;
ip &lt;span class="nt"&gt;-br&lt;/span&gt; a                      &lt;span class="c"&gt;# brief view&lt;/span&gt;
ip &lt;span class="nt"&gt;-4&lt;/span&gt; a show eth0             &lt;span class="c"&gt;# only IPv4 on eth0&lt;/span&gt;
ip r                          &lt;span class="c"&gt;# routing table&lt;/span&gt;
ip &lt;span class="nt"&gt;-br&lt;/span&gt; &lt;span class="nb"&gt;link&lt;/span&gt;                   &lt;span class="c"&gt;# link state of interfaces&lt;/span&gt;
ip neigh                      &lt;span class="c"&gt;# ARP table (neighbors)&lt;/span&gt;

&lt;span class="c"&gt;# Modify&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;ip addr add 10.0.0.10/24 dev eth1
&lt;span class="nb"&gt;sudo &lt;/span&gt;ip addr del 10.0.0.10/24 dev eth1
&lt;span class="nb"&gt;sudo &lt;/span&gt;ip &lt;span class="nb"&gt;link set &lt;/span&gt;eth1 up
&lt;span class="nb"&gt;sudo &lt;/span&gt;ip &lt;span class="nb"&gt;link set &lt;/span&gt;eth1 down
&lt;span class="nb"&gt;sudo &lt;/span&gt;ip route add default via 10.0.0.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  ping &amp;amp; traceroute
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ping &lt;span class="nt"&gt;-c&lt;/span&gt; 4 google.com          &lt;span class="c"&gt;# 4 packets then stop&lt;/span&gt;
ping &lt;span class="nt"&gt;-i&lt;/span&gt; 0.2 &lt;span class="nt"&gt;-c&lt;/span&gt; 20 host        &lt;span class="c"&gt;# 20 packets, 0.2s apart&lt;/span&gt;
ping &lt;span class="nt"&gt;-s&lt;/span&gt; 1472 &lt;span class="nt"&gt;-M&lt;/span&gt; &lt;span class="k"&gt;do &lt;/span&gt;host       &lt;span class="c"&gt;# MTU test (don't fragment)&lt;/span&gt;

traceroute &lt;span class="nt"&gt;-n&lt;/span&gt; 8.8.8.8         &lt;span class="c"&gt;# numeric, no DNS&lt;/span&gt;
traceroute &lt;span class="nt"&gt;-T&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 443 host     &lt;span class="c"&gt;# TCP traceroute on port 443&lt;/span&gt;
mtr &lt;span class="nt"&gt;-rwn&lt;/span&gt; 1.1.1.1              &lt;span class="c"&gt;# report mode, wide, numeric&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  dig (DNS)
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dig example.com               &lt;span class="c"&gt;# default A record&lt;/span&gt;
dig +short A example.com
dig +short MX example.com
dig +short NS example.com
dig +short TXT example.com
dig AAAA example.com          &lt;span class="c"&gt;# IPv6&lt;/span&gt;
dig @8.8.8.8 example.com      &lt;span class="c"&gt;# query specific resolver&lt;/span&gt;
dig +trace example.com        &lt;span class="c"&gt;# full delegation chain&lt;/span&gt;
dig &lt;span class="nt"&gt;-x&lt;/span&gt; 8.8.8.8                &lt;span class="c"&gt;# reverse lookup&lt;/span&gt;
dig +noall +answer example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  curl (essential flags)
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Flag&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-X METHOD&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;HTTP method (GET/POST/PUT/DELETE)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-H "Header: val"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Add request header&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-d 'data'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;POST body&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--data-binary @file&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;POST file contents as body&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-F 'k=v'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;multipart/form-data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-u user:pass&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Basic auth&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-o file&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Output to file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-O&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Save to remote filename&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-L&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Follow redirects&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-I&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;HEAD request, headers only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-i&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Include response headers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-s&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Silent (no progress)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-f&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Fail on HTTP errors (4xx/5xx)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-k&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Insecure (skip cert validation)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-v / --trace-ascii -&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Verbose / full trace&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--resolve host:port:ip&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Override DNS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-w "%{http_code}\n"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print response info&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://api.example.com/v1/users | jq
curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"name":"alice"}'&lt;/span&gt; https://api/x
curl &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s2"&gt;"code=%{http_code} time=%{time_total}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /dev/null &lt;span class="nt"&gt;-s&lt;/span&gt; https://x.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  ss (sockets)
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ss &lt;span class="nt"&gt;-tulnp&lt;/span&gt;                     &lt;span class="c"&gt;# TCP/UDP listening + process&lt;/span&gt;
ss &lt;span class="nt"&gt;-tan&lt;/span&gt;                       &lt;span class="c"&gt;# all TCP&lt;/span&gt;
ss &lt;span class="nt"&gt;-tan&lt;/span&gt; state established
ss &lt;span class="nt"&gt;-tan&lt;/span&gt; state time-wait | &lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt;
ss &lt;span class="nt"&gt;-tnp&lt;/span&gt; &lt;span class="s1"&gt;'( dport = :443 )'&lt;/span&gt;    &lt;span class="c"&gt;# outbound HTTPS&lt;/span&gt;
ss &lt;span class="nt"&gt;-lx&lt;/span&gt;                        &lt;span class="c"&gt;# unix sockets&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  nmap
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nmap &lt;span class="nt"&gt;-sn&lt;/span&gt; 10.0.0.0/24                  &lt;span class="c"&gt;# ping sweep (host discovery)&lt;/span&gt;
nmap &lt;span class="nt"&gt;-p&lt;/span&gt; 22,80,443 host                &lt;span class="c"&gt;# specific ports&lt;/span&gt;
nmap &lt;span class="nt"&gt;-p-&lt;/span&gt; host                         &lt;span class="c"&gt;# all 65535 ports&lt;/span&gt;
nmap &lt;span class="nt"&gt;-sV&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 1-1000 host               &lt;span class="c"&gt;# service version detect&lt;/span&gt;
nmap &lt;span class="nt"&gt;-sS&lt;/span&gt; &lt;span class="nt"&gt;-O&lt;/span&gt; host                      &lt;span class="c"&gt;# SYN scan + OS detect (needs root)&lt;/span&gt;
nmap &lt;span class="nt"&gt;-A&lt;/span&gt; host                          &lt;span class="c"&gt;# aggressive: version+OS+scripts+traceroute&lt;/span&gt;
nmap &lt;span class="nt"&gt;--script&lt;/span&gt; vuln host               &lt;span class="c"&gt;# vulnerability scripts&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  tcpdump
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;tcpdump &lt;span class="nt"&gt;-i&lt;/span&gt; eth0                          &lt;span class="c"&gt;# capture on eth0&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;tcpdump &lt;span class="nt"&gt;-i&lt;/span&gt; any &lt;span class="nt"&gt;-nn&lt;/span&gt; port 80               &lt;span class="c"&gt;# HTTP traffic, numeric&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;tcpdump &lt;span class="nt"&gt;-i&lt;/span&gt; eth0 host 10.0.0.5            &lt;span class="c"&gt;# to/from a host&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;tcpdump &lt;span class="nt"&gt;-i&lt;/span&gt; eth0 &lt;span class="nt"&gt;-w&lt;/span&gt; out.pcap port 443     &lt;span class="c"&gt;# write capture&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;tcpdump &lt;span class="nt"&gt;-r&lt;/span&gt; out.pcap &lt;span class="nt"&gt;-nn&lt;/span&gt;                  &lt;span class="c"&gt;# read capture&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;tcpdump &lt;span class="nt"&gt;-i&lt;/span&gt; eth0 &lt;span class="s1"&gt;'tcp[tcpflags] &amp;amp; (tcp-syn) != 0'&lt;/span&gt;   &lt;span class="c"&gt;# SYN packets&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;tcpdump &lt;span class="nt"&gt;-i&lt;/span&gt; eth0 &lt;span class="nt"&gt;-A&lt;/span&gt; port 80               &lt;span class="c"&gt;# print ASCII payload&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  nc &amp;amp; telnet
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nc &lt;span class="nt"&gt;-zv&lt;/span&gt; host 22 443 8080         &lt;span class="c"&gt;# port check (z=scan, v=verbose)&lt;/span&gt;
nc &lt;span class="nt"&gt;-l&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 9000                   &lt;span class="c"&gt;# listen on port 9000&lt;/span&gt;
nc host 9000                    &lt;span class="c"&gt;# connect (chat)&lt;/span&gt;
nc &lt;span class="nt"&gt;-l&lt;/span&gt; 9000 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; file.bin           &lt;span class="c"&gt;# receive file&lt;/span&gt;
nc host 9000 &amp;lt; file.bin         &lt;span class="c"&gt;# send file&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"GET / HTTP/1.0"&lt;/span&gt; | nc host 80   &lt;span class="c"&gt;# raw HTTP request&lt;/span&gt;
telnet host 25                  &lt;span class="c"&gt;# legacy, still handy for SMTP debug&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  9. Process Management
&lt;/h3&gt;

&lt;h4&gt;
  
  
  ps Options You'll Actually Use
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Output&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ps&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Your shell's processes only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ps aux&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;All processes (BSD): USER PID %CPU %MEM ... COMMAND&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ps -ef&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;All processes (SysV): UID PID PPID C STIME TTY TIME CMD&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ps -eLf&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Include threads (LWP)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ps -fp 1234&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Full info for PID 1234&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ps -ef --forest&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;ASCII tree view&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;`ps -eo pid,user,pri,ni,%cpu,%mem,stat,start,cmd --sort=-%mem&lt;/td&gt;
&lt;td&gt;head`&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  top / htop Keys
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Key&lt;/th&gt;
&lt;th&gt;top&lt;/th&gt;
&lt;th&gt;htop&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;h&lt;/code&gt; / &lt;code&gt;F1&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Help&lt;/td&gt;
&lt;td&gt;Help&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;P&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Sort by CPU&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;M&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Sort by memory&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;F6&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;Choose sort column&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;k&lt;/code&gt; / &lt;code&gt;F9&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Kill (asks PID + signal)&lt;/td&gt;
&lt;td&gt;Kill (signal menu)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;r&lt;/code&gt; / &lt;code&gt;F7/F8&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Renice&lt;/td&gt;
&lt;td&gt;Nice +/-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;F3&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;Search&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;F4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;Filter&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;F5&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;Tree view&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;q&lt;/code&gt; / &lt;code&gt;F10&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Quit&lt;/td&gt;
&lt;td&gt;Quit&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  Signals Table
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Signal&lt;/th&gt;
&lt;th&gt;Num&lt;/th&gt;
&lt;th&gt;Default&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SIGHUP&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Terminate&lt;/td&gt;
&lt;td&gt;Reload config / parent shell exited&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SIGINT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Terminate&lt;/td&gt;
&lt;td&gt;Ctrl+C from terminal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SIGQUIT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Core dump&lt;/td&gt;
&lt;td&gt;Ctrl+\&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SIGILL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Core dump&lt;/td&gt;
&lt;td&gt;Illegal instruction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SIGABRT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Core dump&lt;/td&gt;
&lt;td&gt;abort() called&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SIGKILL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;Terminate&lt;/td&gt;
&lt;td&gt;Force kill (cannot be caught)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SIGSEGV&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;Core dump&lt;/td&gt;
&lt;td&gt;Invalid memory access&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SIGPIPE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;td&gt;Terminate&lt;/td&gt;
&lt;td&gt;Write to closed pipe&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SIGTERM&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;td&gt;Terminate&lt;/td&gt;
&lt;td&gt;Graceful kill (default for &lt;code&gt;kill&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SIGUSR1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;Terminate&lt;/td&gt;
&lt;td&gt;App-defined (often "rotate logs")&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SIGUSR2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;Terminate&lt;/td&gt;
&lt;td&gt;App-defined&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SIGCHLD&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;17&lt;/td&gt;
&lt;td&gt;Ignore&lt;/td&gt;
&lt;td&gt;Child stopped/exited&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SIGCONT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;18&lt;/td&gt;
&lt;td&gt;Continue&lt;/td&gt;
&lt;td&gt;Resume a stopped process&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SIGSTOP&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;19&lt;/td&gt;
&lt;td&gt;Stop&lt;/td&gt;
&lt;td&gt;Stop (cannot be caught)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SIGTSTP&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td&gt;Stop&lt;/td&gt;
&lt;td&gt;Ctrl+Z&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  kill / pkill / killall / pgrep
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;kill &lt;/span&gt;1234                      &lt;span class="c"&gt;# SIGTERM (default)&lt;/span&gt;
&lt;span class="nb"&gt;kill&lt;/span&gt; &lt;span class="nt"&gt;-9&lt;/span&gt; 1234                   &lt;span class="c"&gt;# SIGKILL by number&lt;/span&gt;
&lt;span class="nb"&gt;kill&lt;/span&gt; &lt;span class="nt"&gt;-SIGTERM&lt;/span&gt; 1234             &lt;span class="c"&gt;# by name&lt;/span&gt;
&lt;span class="nb"&gt;kill&lt;/span&gt; &lt;span class="nt"&gt;-HUP&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;pidof nginx&lt;span class="si"&gt;)&lt;/span&gt;       &lt;span class="c"&gt;# reload nginx config&lt;/span&gt;

pgrep &lt;span class="nt"&gt;-f&lt;/span&gt; gunicorn              &lt;span class="c"&gt;# find PIDs by full cmdline&lt;/span&gt;
pgrep &lt;span class="nt"&gt;-af&lt;/span&gt; gunicorn             &lt;span class="c"&gt;# also show cmdline&lt;/span&gt;
pkill &lt;span class="nt"&gt;-f&lt;/span&gt; gunicorn              &lt;span class="c"&gt;# kill all matching&lt;/span&gt;
pkill &lt;span class="nt"&gt;-HUP&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; nginx            &lt;span class="c"&gt;# signal all matching&lt;/span&gt;

killall nginx                  &lt;span class="c"&gt;# kill by exact process name&lt;/span&gt;
killall &lt;span class="nt"&gt;-u&lt;/span&gt; alice               &lt;span class="c"&gt;# kill all of user alice's processes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  nice / renice
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Nice value range: -20 (highest priority) to 19 (lowest)&lt;/span&gt;
&lt;span class="nb"&gt;nice&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; 10 ./batch.sh                &lt;span class="c"&gt;# start with niceness 10&lt;/span&gt;
renice 5 &lt;span class="nt"&gt;-p&lt;/span&gt; 1234                     &lt;span class="c"&gt;# change running process&lt;/span&gt;
renice &lt;span class="nt"&gt;-n&lt;/span&gt; 15 &lt;span class="nt"&gt;-u&lt;/span&gt; alice                &lt;span class="c"&gt;# all of alice's processes&lt;/span&gt;
ionice &lt;span class="nt"&gt;-c&lt;/span&gt; 3 &lt;span class="nt"&gt;-p&lt;/span&gt; 1234                  &lt;span class="c"&gt;# IO scheduling class (3=idle)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  nohup &amp;amp; Job Control
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;nohup&lt;/span&gt; ./long_job.sh &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; out.log 2&amp;gt;&amp;amp;1 &amp;amp;     &lt;span class="c"&gt;# survives logout&lt;/span&gt;
&lt;span class="nb"&gt;disown&lt;/span&gt; %1                                &lt;span class="c"&gt;# detach job from shell&lt;/span&gt;
./job.sh &amp;amp;                               &lt;span class="c"&gt;# background&lt;/span&gt;
&lt;span class="nb"&gt;jobs&lt;/span&gt;                                     &lt;span class="c"&gt;# list shell jobs&lt;/span&gt;
&lt;span class="nb"&gt;jobs&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt;                                  &lt;span class="c"&gt;# with PIDs&lt;/span&gt;
&lt;span class="nb"&gt;fg&lt;/span&gt; %1                                    &lt;span class="c"&gt;# foreground job 1&lt;/span&gt;
&lt;span class="nb"&gt;bg&lt;/span&gt; %1                                    &lt;span class="c"&gt;# resume in background&lt;/span&gt;
Ctrl+Z                                   &lt;span class="c"&gt;# suspend foreground job&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  systemctl
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;systemctl status nginx
systemctl start  nginx
systemctl stop   nginx
systemctl restart nginx
systemctl reload  nginx                  &lt;span class="c"&gt;# signal reload, no restart&lt;/span&gt;
systemctl &lt;span class="nb"&gt;enable&lt;/span&gt;  &lt;span class="nt"&gt;--now&lt;/span&gt; nginx            &lt;span class="c"&gt;# enable + start&lt;/span&gt;
systemctl disable &lt;span class="nt"&gt;--now&lt;/span&gt; nginx
systemctl is-active nginx
systemctl is-enabled nginx
systemctl list-units &lt;span class="nt"&gt;--type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;service
systemctl list-units &lt;span class="nt"&gt;--failed&lt;/span&gt;
systemctl daemon-reload                  &lt;span class="c"&gt;# after editing unit files&lt;/span&gt;
systemctl &lt;span class="nb"&gt;cat &lt;/span&gt;nginx                      &lt;span class="c"&gt;# show unit file&lt;/span&gt;
systemctl edit nginx                     &lt;span class="c"&gt;# override&lt;/span&gt;
systemctl mask nginx                     &lt;span class="c"&gt;# prevent any start (strong disable)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  journalctl
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;journalctl &lt;span class="nt"&gt;-u&lt;/span&gt; nginx                      &lt;span class="c"&gt;# logs for nginx&lt;/span&gt;
journalctl &lt;span class="nt"&gt;-u&lt;/span&gt; nginx &lt;span class="nt"&gt;-f&lt;/span&gt;                   &lt;span class="c"&gt;# follow (like tail -f)&lt;/span&gt;
journalctl &lt;span class="nt"&gt;-u&lt;/span&gt; nginx &lt;span class="nt"&gt;--since&lt;/span&gt; &lt;span class="s2"&gt;"10 min ago"&lt;/span&gt;
journalctl &lt;span class="nt"&gt;-u&lt;/span&gt; nginx &lt;span class="nt"&gt;--since&lt;/span&gt; &lt;span class="s2"&gt;"2026-05-26 09:00"&lt;/span&gt; &lt;span class="nt"&gt;--until&lt;/span&gt; &lt;span class="s2"&gt;"2026-05-26 10:00"&lt;/span&gt;
journalctl &lt;span class="nt"&gt;-u&lt;/span&gt; nginx &lt;span class="nt"&gt;-p&lt;/span&gt; err               &lt;span class="c"&gt;# priority err and above&lt;/span&gt;
journalctl &lt;span class="nt"&gt;-k&lt;/span&gt;                            &lt;span class="c"&gt;# kernel only (= dmesg)&lt;/span&gt;
journalctl &lt;span class="nt"&gt;-b&lt;/span&gt;                            &lt;span class="c"&gt;# current boot&lt;/span&gt;
journalctl &lt;span class="nt"&gt;-b&lt;/span&gt; &lt;span class="nt"&gt;-1&lt;/span&gt;                         &lt;span class="c"&gt;# previous boot&lt;/span&gt;
journalctl &lt;span class="nt"&gt;--disk-usage&lt;/span&gt;
journalctl &lt;span class="nt"&gt;--vacuum-time&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;7d              &lt;span class="c"&gt;# keep only last 7 days&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  10. Cron Expressions
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Field Syntax
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;┌───────────── minute        &lt;span class="o"&gt;(&lt;/span&gt;0 - 59&lt;span class="o"&gt;)&lt;/span&gt;
│ ┌─────────── hour          &lt;span class="o"&gt;(&lt;/span&gt;0 - 23&lt;span class="o"&gt;)&lt;/span&gt;
│ │ ┌───────── day of month  &lt;span class="o"&gt;(&lt;/span&gt;1 - 31&lt;span class="o"&gt;)&lt;/span&gt;
│ │ │ ┌─────── month         &lt;span class="o"&gt;(&lt;/span&gt;1 - 12&lt;span class="o"&gt;)&lt;/span&gt;  or JAN-DEC
│ │ │ │ ┌───── day of week   &lt;span class="o"&gt;(&lt;/span&gt;0 - 6&lt;span class="o"&gt;)&lt;/span&gt;   or SUN-SAT  &lt;span class="o"&gt;(&lt;/span&gt;0 and 7 &lt;span class="o"&gt;=&lt;/span&gt; Sun&lt;span class="o"&gt;)&lt;/span&gt;
│ │ │ │ │
&lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;  command_to_run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Special Characters
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Char&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Every value&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;* * * * *&lt;/code&gt; = every minute&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;,&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;0 9,12,18 * * *&lt;/code&gt; = 9am, 12pm, 6pm&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Range&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;0 9-17 * * 1-5&lt;/code&gt; = 9–5 Mon–Fri&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Step&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;*/15 * * * *&lt;/code&gt; = every 15 minutes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;L&lt;/code&gt; (extended)&lt;/td&gt;
&lt;td&gt;Last&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;0 0 L * *&lt;/code&gt; = last day of month (not standard cron)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;?&lt;/code&gt; (extended)&lt;/td&gt;
&lt;td&gt;No value&lt;/td&gt;
&lt;td&gt;Used in Quartz/Spring&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  20+ Real Examples
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Expression&lt;/th&gt;
&lt;th&gt;Runs&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;* * * * *&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Every minute&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;*/5 * * * *&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Every 5 minutes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;*/15 * * * *&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Every 15 minutes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;0 * * * *&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Every hour on the hour&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;0 */2 * * *&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Every 2 hours&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;30 3 * * *&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;3:30 AM every day&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;0 0 * * *&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Midnight every day&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;0 9 * * 1-5&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;9 AM weekdays&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;0 9 * * 1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;9 AM every Monday&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;0 9 * * 0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;9 AM every Sunday&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;0 9-17 * * 1-5&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Every hour, 9–5, weekdays&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;0 0 1 * *&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Midnight on 1st of month&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;0 0 1 1 *&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Midnight on Jan 1 (yearly)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;0 0 1,15 * *&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1st and 15th at midnight&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;0 2 * * 6&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;2 AM every Saturday (weekly backup)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;15 14 1 * *&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;2:15 PM on the 1st of each month&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;0 22 * * 1-5&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;10 PM weekdays&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;5 4 * * SUN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;4:05 AM every Sunday&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;0 4 1-7 * 1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;4 AM on first Monday of month (kind of)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;*/10 9-17 * * 1-5&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Every 10 min, business hours, weekdays&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;0 0 */3 * *&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Midnight every 3 days&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;0 12 * * 1#1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Noon on first Monday (extended/Quartz)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  @ Shortcuts
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Shortcut&lt;/th&gt;
&lt;th&gt;Equivalent&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@reboot&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;Once at startup&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;@yearly&lt;/code&gt; / &lt;code&gt;@annually&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0 0 1 1 *&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Once a year&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@monthly&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0 0 1 * *&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Once a month&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@weekly&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0 0 * * 0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Once a week (Sunday)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;@daily&lt;/code&gt; / &lt;code&gt;@midnight&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0 0 * * *&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Once a day&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@hourly&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0 * * * *&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Once an hour&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  Managing Crontabs
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;crontab &lt;span class="nt"&gt;-l&lt;/span&gt;                    &lt;span class="c"&gt;# list current user's crontab&lt;/span&gt;
crontab &lt;span class="nt"&gt;-e&lt;/span&gt;                    &lt;span class="c"&gt;# edit (uses $EDITOR)&lt;/span&gt;
crontab &lt;span class="nt"&gt;-r&lt;/span&gt;                    &lt;span class="c"&gt;# remove (be careful!)&lt;/span&gt;
crontab &lt;span class="nt"&gt;-u&lt;/span&gt; alice &lt;span class="nt"&gt;-l&lt;/span&gt;           &lt;span class="c"&gt;# list alice's crontab (need root)&lt;/span&gt;

&lt;span class="c"&gt;# System-wide files:&lt;/span&gt;
/etc/crontab                  &lt;span class="c"&gt;# has a 'user' column&lt;/span&gt;
/etc/cron.d/&lt;span class="k"&gt;*&lt;/span&gt;                 &lt;span class="c"&gt;# drop-in files (also need user column)&lt;/span&gt;
/etc/cron.&lt;span class="o"&gt;{&lt;/span&gt;hourly,daily,weekly,monthly&lt;span class="o"&gt;}&lt;/span&gt;/   &lt;span class="c"&gt;# scripts dir (no schedule)&lt;/span&gt;

&lt;span class="c"&gt;# Always set env + redirect output in cron jobs:&lt;/span&gt;
&lt;span class="nv"&gt;SHELL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/bin/bash
&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
&lt;span class="nv"&gt;MAILTO&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ops@1buy.ai

0 3 &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; /opt/scripts/backup.sh &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /var/log/backup.log 2&amp;gt;&amp;amp;1

&lt;span class="c"&gt;# Useful for verifying: test online at crontab.guru&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Keyboard Shortcuts and Aliases
&lt;/h2&gt;

&lt;p&gt;Speed comes from muscle memory, not from typing faster. &lt;em&gt;Until these shortcuts live in your fingers, you won't feel the productivity boost.&lt;/em&gt; Pick 10 from this list, drill them this week, then come back for the next 10.&lt;/p&gt;

&lt;h3&gt;
  
  
  Terminal Keyboard Shortcuts (Readline / Bash)
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Cursor &amp;amp; Editing
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Shortcut&lt;/th&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Ctrl+A&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Move to beginning of line&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Ctrl+E&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Move to end of line&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;Ctrl+B&lt;/code&gt; / &lt;code&gt;Ctrl+F&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Back / forward one character&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;Alt+B&lt;/code&gt; / &lt;code&gt;Alt+F&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Back / forward one word&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Ctrl+U&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Cut from cursor to beginning of line&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Ctrl+K&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Cut from cursor to end of line&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Ctrl+W&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Cut previous word&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Alt+D&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Cut next word&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Ctrl+Y&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Paste (yank) last cut&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Ctrl+T&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Swap current and previous character&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Alt+T&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Swap current and previous word&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Ctrl+_&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Undo last edit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Ctrl+L&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Clear screen (keeps current line)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  Process &amp;amp; Session
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Shortcut&lt;/th&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Ctrl+C&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Send SIGINT (interrupt) to foreground process&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Ctrl+Z&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Send SIGTSTP (suspend) — resume with &lt;code&gt;fg&lt;/code&gt; or &lt;code&gt;bg&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Ctrl+D&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;EOF — exits shell if line is empty&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Ctrl+S&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Freeze terminal output (XOFF) — try not to hit this&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Ctrl+Q&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Resume frozen terminal (XON)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Ctrl+\&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;SIGQUIT — like Ctrl+C but with core dump&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  History &amp;amp; Search
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Shortcut&lt;/th&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Ctrl+R&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Reverse-i-search through history (press again for next match)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Ctrl+G&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Cancel reverse search&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;Ctrl+P&lt;/code&gt; / &lt;code&gt;Ctrl+N&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Previous / next history entry&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Alt+.&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Insert last argument of previous command (press repeatedly)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  History Expansion (the bang stuff)
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Expansion&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;!!&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Previous command in full (e.g. &lt;code&gt;sudo !!&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;!$&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Last argument of previous command&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;!*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;All arguments of previous command&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;!N&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Command number N from history&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;!-N&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;N commands back&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;!abc&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Most recent command starting with &lt;code&gt;abc&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;!?abc?&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Most recent command containing &lt;code&gt;abc&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;^old^new&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Re-run previous command, replacing first &lt;code&gt;old&lt;/code&gt; with &lt;code&gt;new&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;!!:gs/old/new/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Re-run previous command, global substitution&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Forgot sudo?&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;apt update
E: Permission denied
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo&lt;/span&gt; &lt;span class="o"&gt;!!&lt;/span&gt;         &lt;span class="c"&gt;# expands to:  sudo apt update&lt;/span&gt;

&lt;span class="c"&gt;# Typo in long command?&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;systemctl restrat nginx
&lt;span class="nv"&gt;$ &lt;/span&gt;^restrat^restart

&lt;span class="c"&gt;# Reuse last argument&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; /opt/myapp
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;          &lt;span class="c"&gt;# cd /opt/myapp&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Bash History Management
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# View / search&lt;/span&gt;
&lt;span class="nb"&gt;history&lt;/span&gt;                   &lt;span class="c"&gt;# show full history&lt;/span&gt;
&lt;span class="nb"&gt;history &lt;/span&gt;20                &lt;span class="c"&gt;# last 20&lt;/span&gt;
&lt;span class="nb"&gt;history&lt;/span&gt; | &lt;span class="nb"&gt;grep &lt;/span&gt;ssh        &lt;span class="c"&gt;# filter&lt;/span&gt;

&lt;span class="c"&gt;# Re-run by number&lt;/span&gt;
&lt;span class="o"&gt;!&lt;/span&gt;457                      &lt;span class="c"&gt;# run history entry 457&lt;/span&gt;
&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nt"&gt;-2&lt;/span&gt;                       &lt;span class="c"&gt;# run 2 commands ago&lt;/span&gt;

&lt;span class="c"&gt;# Delete entries&lt;/span&gt;
&lt;span class="nb"&gt;history&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; 457            &lt;span class="c"&gt;# delete entry 457&lt;/span&gt;
&lt;span class="nb"&gt;history&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt;                &lt;span class="c"&gt;# clear ALL history (session)&lt;/span&gt;
&lt;span class="nb"&gt;history&lt;/span&gt; &lt;span class="nt"&gt;-w&lt;/span&gt;                &lt;span class="c"&gt;# write current session to file&lt;/span&gt;

&lt;span class="c"&gt;# Tune behavior in ~/.bashrc&lt;/span&gt;
&lt;span class="nv"&gt;HISTSIZE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;10000                       &lt;span class="c"&gt;# in-memory entries&lt;/span&gt;
&lt;span class="nv"&gt;HISTFILESIZE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;20000                   &lt;span class="c"&gt;# on-disk entries&lt;/span&gt;
&lt;span class="nv"&gt;HISTCONTROL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ignoreboth:erasedups     &lt;span class="c"&gt;# skip dupes &amp;amp; lines starting with space&lt;/span&gt;
&lt;span class="nv"&gt;HISTTIMEFORMAT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"%F %T "&lt;/span&gt;              &lt;span class="c"&gt;# add timestamps&lt;/span&gt;
&lt;span class="nv"&gt;HISTIGNORE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"ls:ll:pwd:exit:clear:history"&lt;/span&gt;   &lt;span class="c"&gt;# never record these&lt;/span&gt;
&lt;span class="nb"&gt;shopt&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; histappend                  &lt;span class="c"&gt;# append, don't overwrite&lt;/span&gt;
&lt;span class="nb"&gt;shopt&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; cmdhist                     &lt;span class="c"&gt;# multi-line commands as one entry&lt;/span&gt;

&lt;span class="c"&gt;# Share history across sessions in real time:&lt;/span&gt;
&lt;span class="nv"&gt;PROMPT_COMMAND&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'history -a; history -n'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Prepending a command with a space (when &lt;code&gt;HISTCONTROL&lt;/code&gt; includes &lt;code&gt;ignorespace&lt;/code&gt; or &lt;code&gt;ignoreboth&lt;/code&gt;) keeps it out of history — handy when typing one-off commands with secrets.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Tab Completion
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Trigger&lt;/th&gt;
&lt;th&gt;What it completes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;Tab&lt;/code&gt; (once)&lt;/td&gt;
&lt;td&gt;Complete if unambiguous&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;Tab Tab&lt;/code&gt; (double)&lt;/td&gt;
&lt;td&gt;Show all possible completions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;After &lt;code&gt;cd&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Directories only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;After &lt;code&gt;ssh&lt;/code&gt; / &lt;code&gt;scp&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Hostnames from &lt;code&gt;~/.ssh/config&lt;/code&gt; and &lt;code&gt;~/.ssh/known_hosts&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;After &lt;code&gt;$&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Environment variable names&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;After &lt;code&gt;~&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Usernames (for home dirs)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;After &lt;code&gt;git&lt;/code&gt;, &lt;code&gt;kubectl&lt;/code&gt;, &lt;code&gt;docker&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Subcommands, flags, resources (with completion installed)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Enable programmable completion (most distros do this by default)&lt;/span&gt;
&lt;span class="c"&gt;# /etc/bash_completion or in ~/.bashrc:&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; /usr/share/bash-completion/bash_completion &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nb"&gt;.&lt;/span&gt; /usr/share/bash-completion/bash_completion

&lt;span class="c"&gt;# Add tool-specific completion&lt;/span&gt;
&lt;span class="nb"&gt;source&lt;/span&gt; &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;kubectl completion bash&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;source&lt;/span&gt; &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;helm completion bash&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;complete&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt; __start_kubectl k        &lt;span class="c"&gt;# so 'k &amp;lt;tab&amp;gt;' also works&lt;/span&gt;

&lt;span class="c"&gt;# Show completions for a command&lt;/span&gt;
&lt;span class="nb"&gt;complete&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  20+ Useful Aliases for ~/.bashrc
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# ---------- Navigation &amp;amp; Listing ----------&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;ll&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'ls -lah --color=auto'&lt;/span&gt;           &lt;span class="c"&gt;# long, human, hidden, colored&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;la&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'ls -A --color=auto'&lt;/span&gt;             &lt;span class="c"&gt;# show dotfiles (no . and ..)&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;l&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'ls -CF --color=auto'&lt;/span&gt;             &lt;span class="c"&gt;# quick column listing&lt;/span&gt;
&lt;span class="nb"&gt;alias&lt;/span&gt; ..&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'cd ..'&lt;/span&gt;                          &lt;span class="c"&gt;# up one&lt;/span&gt;
&lt;span class="nb"&gt;alias&lt;/span&gt; ...&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'cd ../..'&lt;/span&gt;                      &lt;span class="c"&gt;# up two&lt;/span&gt;
&lt;span class="nb"&gt;alias&lt;/span&gt; ....&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'cd ../../..'&lt;/span&gt;                  &lt;span class="c"&gt;# up three&lt;/span&gt;
&lt;span class="nb"&gt;alias&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; -&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'cd -'&lt;/span&gt;                         &lt;span class="c"&gt;# toggle to previous dir&lt;/span&gt;

&lt;span class="c"&gt;# ---------- Safety ----------&lt;/span&gt;
&lt;span class="nb"&gt;alias rm&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'rm -i'&lt;/span&gt;                          &lt;span class="c"&gt;# ask before remove&lt;/span&gt;
&lt;span class="nb"&gt;alias cp&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'cp -i'&lt;/span&gt;                          &lt;span class="c"&gt;# ask before overwrite&lt;/span&gt;
&lt;span class="nb"&gt;alias mv&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'mv -i'&lt;/span&gt;                          &lt;span class="c"&gt;# ask before overwrite&lt;/span&gt;
&lt;span class="nb"&gt;alias mkdir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'mkdir -pv'&lt;/span&gt;                   &lt;span class="c"&gt;# parents + verbose&lt;/span&gt;

&lt;span class="c"&gt;# ---------- Search / Filters ----------&lt;/span&gt;
&lt;span class="nb"&gt;alias grep&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'grep --color=auto'&lt;/span&gt;            &lt;span class="c"&gt;# colored matches&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;egrep&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'egrep --color=auto'&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;fgrep&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'fgrep --color=auto'&lt;/span&gt;

&lt;span class="c"&gt;# ---------- Disk / System ----------&lt;/span&gt;
&lt;span class="nb"&gt;alias df&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'df -hT'&lt;/span&gt;                         &lt;span class="c"&gt;# human + filesystem type&lt;/span&gt;
&lt;span class="nb"&gt;alias du&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'du -h --max-depth=1'&lt;/span&gt;            &lt;span class="c"&gt;# human, one level&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;free&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'free -h'&lt;/span&gt;                      &lt;span class="c"&gt;# human memory&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;ports&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'ss -tulnp'&lt;/span&gt;                   &lt;span class="c"&gt;# what's listening?&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;psg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'ps aux | grep -v grep | grep -iE'&lt;/span&gt;  &lt;span class="c"&gt;# process search&lt;/span&gt;

&lt;span class="c"&gt;# ---------- Network ----------&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;myip&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'curl -s https://ifconfig.me &amp;amp;&amp;amp; echo'&lt;/span&gt;   &lt;span class="c"&gt;# public IP&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;localip&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"ip -4 -br a | grep -v '^lo'"&lt;/span&gt;        &lt;span class="c"&gt;# local IPs&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;weather&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'curl -s wttr.in/?format=3'&lt;/span&gt;          &lt;span class="c"&gt;# quick weather&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'curl -sI'&lt;/span&gt;                           &lt;span class="c"&gt;# response headers only&lt;/span&gt;

&lt;span class="c"&gt;# ---------- Git ----------&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;g&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'git'&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;gs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'git status -sb'&lt;/span&gt;                 &lt;span class="c"&gt;# short branch + status&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;gco&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'git checkout'&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;gcm&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'git commit -m'&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;gp&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'git pull --rebase'&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;gpush&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'git push'&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;gl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'git log --oneline --graph --decorate -20'&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;gd&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'git diff'&lt;/span&gt;

&lt;span class="c"&gt;# ---------- DevOps Tools ----------&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;k&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'kubectl'&lt;/span&gt;                         &lt;span class="c"&gt;# the universal short form&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;kgp&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'kubectl get pods'&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;kgs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'kubectl get svc'&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;kdp&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'kubectl describe pod'&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;kx&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'kubectl config use-context'&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;kns&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'kubectl config set-context --current --namespace'&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;d&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'docker'&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;dc&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'docker compose'&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;dps&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"'&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;tf&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'terraform'&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;jc&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'journalctl -u'&lt;/span&gt;                  &lt;span class="c"&gt;# usage: jc nginx -f&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;sc&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'systemctl'&lt;/span&gt;                      &lt;span class="c"&gt;# usage: sc status nginx&lt;/span&gt;

&lt;span class="c"&gt;# ---------- Misc Quality of Life ----------&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'echo -e ${PATH//:/\\n}'&lt;/span&gt;       &lt;span class="c"&gt;# print PATH one entry per line&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;now&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'date "+%Y-%m-%d %H:%M:%S"'&lt;/span&gt;     &lt;span class="c"&gt;# quick timestamp&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;reload&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'source ~/.bashrc'&lt;/span&gt;           &lt;span class="c"&gt;# re-source after edits&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;ports-listening&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'sudo lsof -i -P -n | grep LISTEN'&lt;/span&gt;
&lt;span class="nb"&gt;alias&lt;/span&gt; ..git&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'cd $(git rev-parse --show-toplevel)'&lt;/span&gt;  &lt;span class="c"&gt;# jump to repo root&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Rationale.&lt;/strong&gt; Aliases like &lt;code&gt;ll&lt;/code&gt;, &lt;code&gt;..&lt;/code&gt;, &lt;code&gt;k&lt;/code&gt;, &lt;code&gt;d&lt;/code&gt;, &lt;code&gt;gs&lt;/code&gt; shave 5–10 keystrokes off the dozens of times you type them daily. Safety aliases (&lt;code&gt;rm -i&lt;/code&gt;, &lt;code&gt;cp -i&lt;/code&gt;) save you from career-ending mistakes — yes, even seniors have done &lt;code&gt;rm -rf /&lt;/code&gt; at 2 AM. The &lt;code&gt;myip&lt;/code&gt;/&lt;code&gt;localip&lt;/code&gt;/&lt;code&gt;ports&lt;/code&gt; ones replace "wait, what's that command again?" with one word.&lt;/p&gt;

&lt;p&gt;To make aliases work in non-interactive contexts (rare but useful for scripts), put them in &lt;code&gt;~/.bash_aliases&lt;/code&gt; and source that file from &lt;code&gt;~/.bashrc&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Useful Bash Functions
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# --- mkcd: make a directory and cd into it in one step ---&lt;/span&gt;
mkcd&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Usage: mkcd &amp;lt;dir&amp;gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
    &lt;span class="k"&gt;return &lt;/span&gt;1
  &lt;span class="k"&gt;fi
  &lt;/span&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="nt"&gt;-P&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="c"&gt;# Usage:  mkcd /opt/projects/new-app&lt;/span&gt;

&lt;span class="c"&gt;# --- extract: smart extractor for any common archive ---&lt;/span&gt;
extract&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Usage: extract &amp;lt;archive&amp;gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
    &lt;span class="k"&gt;return &lt;/span&gt;1
  &lt;span class="k"&gt;fi
  if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"extract: '&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;' is not a regular file"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
    &lt;span class="k"&gt;return &lt;/span&gt;1
  &lt;span class="k"&gt;fi
  case&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
    &lt;span class="k"&gt;*&lt;/span&gt;.tar.bz2|&lt;span class="k"&gt;*&lt;/span&gt;.tbz2&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-xjvf&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;   &lt;span class="p"&gt;;;&lt;/span&gt;
    &lt;span class="k"&gt;*&lt;/span&gt;.tar.gz|&lt;span class="k"&gt;*&lt;/span&gt;.tgz&lt;span class="p"&gt;)&lt;/span&gt;     &lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-xzvf&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;   &lt;span class="p"&gt;;;&lt;/span&gt;
    &lt;span class="k"&gt;*&lt;/span&gt;.tar.xz|&lt;span class="k"&gt;*&lt;/span&gt;.txz&lt;span class="p"&gt;)&lt;/span&gt;     &lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-xJvf&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;   &lt;span class="p"&gt;;;&lt;/span&gt;
    &lt;span class="k"&gt;*&lt;/span&gt;.tar.zst&lt;span class="p"&gt;)&lt;/span&gt;          &lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;--zstd&lt;/span&gt; &lt;span class="nt"&gt;-xvf&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
    &lt;span class="k"&gt;*&lt;/span&gt;.tar&lt;span class="p"&gt;)&lt;/span&gt;              &lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-xvf&lt;/span&gt;  &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;   &lt;span class="p"&gt;;;&lt;/span&gt;
    &lt;span class="k"&gt;*&lt;/span&gt;.bz2&lt;span class="p"&gt;)&lt;/span&gt;              bunzip2   &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;   &lt;span class="p"&gt;;;&lt;/span&gt;
    &lt;span class="k"&gt;*&lt;/span&gt;.gz&lt;span class="p"&gt;)&lt;/span&gt;               &lt;span class="nb"&gt;gunzip&lt;/span&gt;    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;   &lt;span class="p"&gt;;;&lt;/span&gt;
    &lt;span class="k"&gt;*&lt;/span&gt;.xz&lt;span class="p"&gt;)&lt;/span&gt;               unxz      &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;   &lt;span class="p"&gt;;;&lt;/span&gt;
    &lt;span class="k"&gt;*&lt;/span&gt;.zip&lt;span class="p"&gt;)&lt;/span&gt;              unzip     &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;   &lt;span class="p"&gt;;;&lt;/span&gt;
    &lt;span class="k"&gt;*&lt;/span&gt;.rar&lt;span class="p"&gt;)&lt;/span&gt;              unrar x   &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;   &lt;span class="p"&gt;;;&lt;/span&gt;
    &lt;span class="k"&gt;*&lt;/span&gt;.7z&lt;span class="p"&gt;)&lt;/span&gt;               7z x      &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;   &lt;span class="p"&gt;;;&lt;/span&gt;
    &lt;span class="k"&gt;*&lt;/span&gt;.Z&lt;span class="p"&gt;)&lt;/span&gt;                uncompress &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;  &lt;span class="p"&gt;;;&lt;/span&gt;
    &lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"extract: don't know how to handle '&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;'"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;return &lt;/span&gt;1 &lt;span class="p"&gt;;;&lt;/span&gt;
  &lt;span class="k"&gt;esac&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="c"&gt;# Usage:  extract release-v2.4.1.tar.gz&lt;/span&gt;

&lt;span class="c"&gt;# --- bak: quick timestamped backup of a file ---&lt;/span&gt;
bak&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Usage: bak &amp;lt;file&amp;gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
    &lt;span class="k"&gt;return &lt;/span&gt;1
  &lt;span class="k"&gt;fi
  if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"bak: '&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;' does not exist"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
    &lt;span class="k"&gt;return &lt;/span&gt;1
  &lt;span class="k"&gt;fi
  &lt;/span&gt;&lt;span class="nb"&gt;local &lt;/span&gt;ts
  &lt;span class="nv"&gt;ts&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%Y%m%d-%H%M%S&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;.bak.&lt;/span&gt;&lt;span class="nv"&gt;$ts&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Backed up to &lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;.bak.&lt;/span&gt;&lt;span class="nv"&gt;$ts&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="c"&gt;# Usage:  bak /etc/nginx/nginx.conf&lt;/span&gt;
&lt;span class="c"&gt;#         -&amp;gt; /etc/nginx/nginx.conf.bak.20260526-101530&lt;/span&gt;

&lt;span class="c"&gt;# --- Bonus: cdf -- cd into the directory of a file ---&lt;/span&gt;
cdf&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Usage: cdf &amp;lt;file&amp;gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
    &lt;span class="k"&gt;return &lt;/span&gt;1
  &lt;span class="k"&gt;fi
  &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# --- Bonus: fkill -- fuzzy kill (needs fzf) ---&lt;/span&gt;
fkill&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;pid
  &lt;span class="nv"&gt;pid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;ps &lt;span class="nt"&gt;-ef&lt;/span&gt; | &lt;span class="nb"&gt;sed &lt;/span&gt;1d | fzf &lt;span class="nt"&gt;-m&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $2}'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$pid&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$pid&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | xargs &lt;span class="nb"&gt;kill&lt;/span&gt; -&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;1&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;15&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Drop these into &lt;code&gt;~/.bashrc&lt;/code&gt; (or a sourced &lt;code&gt;~/.bash_functions&lt;/code&gt; file), run &lt;code&gt;source ~/.bashrc&lt;/code&gt;, and they become first-class commands. The &lt;code&gt;extract&lt;/code&gt; function alone will save you "wait, was it &lt;code&gt;xjvf&lt;/code&gt; or &lt;code&gt;xzvf&lt;/code&gt;?" lookups for the rest of your career. &lt;em&gt;Set it up once, and it pays off for life.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Advanced Linux for DevOps
&lt;/h2&gt;

&lt;p&gt;This is where the real DevOps game begins. Containers, kernel tuning, boot internals, LVM, RAID — this is the layer that completes the transition from frontend developer to six-figure (USD) DevOps engineer. An interviewer will ask you: "How does a Docker container work under the hood?" — if you can explain namespaces + cgroups + overlayfs + capabilities, you're already in the top 20% of candidates.&lt;/p&gt;

&lt;h3&gt;
  
  
  Containers Are Not Magic — They Are Linux Features
&lt;/h3&gt;

&lt;p&gt;A container = &lt;strong&gt;namespaces (isolation) + cgroups (resource limits) + overlayfs (layered FS) + capabilities (privilege control)&lt;/strong&gt;. Docker, containerd, Podman — they all sit on this foundation. You don't need a full hypervisor like a VM; the kernel is shared with the host, but each process sees an isolated world as if it had its own OS.&lt;/p&gt;

&lt;h3&gt;
  
  
  Linux Namespaces — A Process Gets Its Own Universe
&lt;/h3&gt;

&lt;p&gt;Namespaces show a process group only a subset of the system. Inside a container, a process thinks it's PID 1, with its own network stack and its own filesystem root.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Namespace&lt;/th&gt;
&lt;th&gt;Flag&lt;/th&gt;
&lt;th&gt;What It Isolates&lt;/th&gt;
&lt;th&gt;Practical Effect&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pid&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CLONE_NEWPID&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Process IDs&lt;/td&gt;
&lt;td&gt;The container's PID 1 might be PID 14523 on the host&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mnt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CLONE_NEWNS&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Mount points / filesystem view&lt;/td&gt;
&lt;td&gt;The container sees its own &lt;code&gt;/&lt;/code&gt;, not the host's &lt;code&gt;/&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;net&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CLONE_NEWNET&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Network interfaces, routes, iptables, sockets&lt;/td&gt;
&lt;td&gt;Its own eth0, its own loopback, its own iptables rules&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ipc&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CLONE_NEWIPC&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;SysV IPC, POSIX message queues&lt;/td&gt;
&lt;td&gt;Shared memory segments isolated&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;uts&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CLONE_NEWUTS&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Hostname, domain name&lt;/td&gt;
&lt;td&gt;Each container gets its own hostname (&lt;code&gt;hostname&lt;/code&gt; command)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;user&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CLONE_NEWUSER&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;UID/GID mappings&lt;/td&gt;
&lt;td&gt;The container's root (UID 0) maps to unprivileged user 100000 on the host&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cgroup&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CLONE_NEWCGROUP&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Cgroup root view&lt;/td&gt;
&lt;td&gt;The container sees only a subset of the cgroup hierarchy&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Manual Namespace Creation — Build a Container Without Docker
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create a new PID + network + mount namespace&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;unshare &lt;span class="nt"&gt;--pid&lt;/span&gt; &lt;span class="nt"&gt;--net&lt;/span&gt; &lt;span class="nt"&gt;--mount&lt;/span&gt; &lt;span class="nt"&gt;--uts&lt;/span&gt; &lt;span class="nt"&gt;--ipc&lt;/span&gt; &lt;span class="nt"&gt;--fork&lt;/span&gt; &lt;span class="nt"&gt;--mount-proc&lt;/span&gt; bash

&lt;span class="c"&gt;# Go inside and check&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$$&lt;/span&gt;                    &lt;span class="c"&gt;# Shows PID 1 (it's something else on the host)&lt;/span&gt;
&lt;span class="nb"&gt;hostname &lt;/span&gt;container-demo    &lt;span class="c"&gt;# Changes only inside this namespace&lt;/span&gt;
ps aux                     &lt;span class="c"&gt;# Only the processes in this namespace&lt;/span&gt;
ip addr                    &lt;span class="c"&gt;# Only loopback (network is isolated)&lt;/span&gt;

&lt;span class="c"&gt;# Everything is cleaned up on exit&lt;/span&gt;
&lt;span class="nb"&gt;exit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is exactly what Docker does — it just adds an image, overlayfs, and orchestration on top.&lt;/p&gt;

&lt;h3&gt;
  
  
  Inspect Namespaces — lsns and /proc
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# List all namespaces&lt;/span&gt;
lsns

&lt;span class="c"&gt;# Specific type&lt;/span&gt;
lsns &lt;span class="nt"&gt;-t&lt;/span&gt; net
lsns &lt;span class="nt"&gt;-t&lt;/span&gt; pid

&lt;span class="c"&gt;# Look at your own shell's namespaces&lt;/span&gt;
&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; /proc/&lt;span class="nv"&gt;$$&lt;/span&gt;/ns/
&lt;span class="c"&gt;# Output:&lt;/span&gt;
&lt;span class="c"&gt;# lrwxrwxrwx 1 user user 0 May 26 10:23 cgroup -&amp;gt; 'cgroup:[4026531835]'&lt;/span&gt;
&lt;span class="c"&gt;# lrwxrwxrwx 1 user user 0 May 26 10:23 ipc    -&amp;gt; 'ipc:[4026531839]'&lt;/span&gt;
&lt;span class="c"&gt;# lrwxrwxrwx 1 user user 0 May 26 10:23 mnt    -&amp;gt; 'mnt:[4026531840]'&lt;/span&gt;
&lt;span class="c"&gt;# lrwxrwxrwx 1 user user 0 May 26 10:23 net    -&amp;gt; 'net:[4026531992]'&lt;/span&gt;
&lt;span class="c"&gt;# lrwxrwxrwx 1 user user 0 May 26 10:23 pid    -&amp;gt; 'pid:[4026531836]'&lt;/span&gt;
&lt;span class="c"&gt;# lrwxrwxrwx 1 user user 0 May 26 10:23 user   -&amp;gt; 'user:[4026531837]'&lt;/span&gt;
&lt;span class="c"&gt;# lrwxrwxrwx 1 user user 0 May 26 10:23 uts    -&amp;gt; 'uts:[4026531838]'&lt;/span&gt;

&lt;span class="c"&gt;# Enter a container's namespace (once you have the Docker PID)&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;nsenter &lt;span class="nt"&gt;--target&lt;/span&gt; 14523 &lt;span class="nt"&gt;--pid&lt;/span&gt; &lt;span class="nt"&gt;--net&lt;/span&gt; &lt;span class="nt"&gt;--mount&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  cgroups v2 (Ubuntu 24.04 Default)
&lt;/h3&gt;

&lt;p&gt;cgroups (control groups) limit and account for the &lt;strong&gt;resource usage&lt;/strong&gt; of processes. Ubuntu 24.04 uses the unified cgroup v2 — a single hierarchy at &lt;code&gt;/sys/fs/cgroup&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Verify cgroup v2&lt;/span&gt;
mount | &lt;span class="nb"&gt;grep &lt;/span&gt;cgroup
&lt;span class="c"&gt;# cgroup2 on /sys/fs/cgroup type cgroup2 (rw,nosuid,nodev,noexec,relatime,nsdelegate)&lt;/span&gt;

&lt;span class="c"&gt;# Available controllers&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; /sys/fs/cgroup/cgroup.controllers
&lt;span class="c"&gt;# cpu io memory pids hugetlb cpuset rdma misc&lt;/span&gt;

&lt;span class="c"&gt;# Create a new cgroup and set limits&lt;/span&gt;
&lt;span class="nb"&gt;sudo mkdir&lt;/span&gt; /sys/fs/cgroup/myapp
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"+cpu +memory +io +pids"&lt;/span&gt; | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; /sys/fs/cgroup/cgroup.subtree_control

&lt;span class="c"&gt;# CPU: 50% of one core (50000 us out of 100000 us period)&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"50000 100000"&lt;/span&gt; | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; /sys/fs/cgroup/myapp/cpu.max

&lt;span class="c"&gt;# Memory: 512 MB hard limit&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"536870912"&lt;/span&gt; | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; /sys/fs/cgroup/myapp/memory.max

&lt;span class="c"&gt;# PIDs: max 100 processes&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"100"&lt;/span&gt; | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; /sys/fs/cgroup/myapp/pids.max

&lt;span class="c"&gt;# IO: weight (10-1000, default 100), per-device limits also possible&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"default 50"&lt;/span&gt; | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; /sys/fs/cgroup/myapp/io.weight

&lt;span class="c"&gt;# Put a process into the cgroup&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$$&lt;/span&gt; | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; /sys/fs/cgroup/myapp/cgroup.procs

&lt;span class="c"&gt;# Check current usage&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; /sys/fs/cgroup/myapp/memory.current
&lt;span class="nb"&gt;cat&lt;/span&gt; /sys/fs/cgroup/myapp/cpu.stat
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Docker's &lt;code&gt;--memory 512m --cpus="0.5"&lt;/code&gt; writes to exactly these cgroup files internally. A Kubernetes pod's &lt;code&gt;resources.limits&lt;/code&gt; also end up here via kubelet → containerd → runc.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  How Docker Stitches It All Together
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Technology&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Isolation&lt;/td&gt;
&lt;td&gt;Namespaces (pid, net, mnt, uts, ipc, user)&lt;/td&gt;
&lt;td&gt;Each process in its own universe&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Resource limits&lt;/td&gt;
&lt;td&gt;cgroups v2&lt;/td&gt;
&lt;td&gt;CPU, memory, IO, PIDs cap&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Filesystem&lt;/td&gt;
&lt;td&gt;overlayfs (lowerdir + upperdir = merged)&lt;/td&gt;
&lt;td&gt;Image layers + writable container layer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Privileges&lt;/td&gt;
&lt;td&gt;Capabilities (drop most by default)&lt;/td&gt;
&lt;td&gt;Container root != host root&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Security&lt;/td&gt;
&lt;td&gt;seccomp, AppArmor/SELinux&lt;/td&gt;
&lt;td&gt;Syscall filtering, MAC&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Networking&lt;/td&gt;
&lt;td&gt;veth pair + bridge (docker0) + iptables&lt;/td&gt;
&lt;td&gt;Container-to-host-to-internet&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Try overlayfs yourself&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /tmp/ovl/&lt;span class="o"&gt;{&lt;/span&gt;lower,upper,work,merged&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"from image"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /tmp/ovl/lower/file.txt

&lt;span class="nb"&gt;sudo &lt;/span&gt;mount &lt;span class="nt"&gt;-t&lt;/span&gt; overlay overlay &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;lowerdir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/tmp/ovl/lower,upperdir&lt;span class="o"&gt;=&lt;/span&gt;/tmp/ovl/upper,workdir&lt;span class="o"&gt;=&lt;/span&gt;/tmp/ovl/work &lt;span class="se"&gt;\&lt;/span&gt;
  /tmp/ovl/merged

&lt;span class="nb"&gt;cat&lt;/span&gt; /tmp/ovl/merged/file.txt              &lt;span class="c"&gt;# "from image"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"container write"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /tmp/ovl/merged/file.txt
&lt;span class="nb"&gt;cat&lt;/span&gt; /tmp/ovl/lower/file.txt               &lt;span class="c"&gt;# Still "from image" — read-only&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; /tmp/ovl/upper/file.txt               &lt;span class="c"&gt;# "container write" — copy-on-write&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Linux Capabilities — The Modern Replacement for Setuid
&lt;/h3&gt;

&lt;p&gt;In old Unix, a binary was either root (UID 0, all-powerful) or a normal user. Linux capabilities break root's powers into 40+ smaller chunks. &lt;code&gt;ping&lt;/code&gt; needs a raw socket — it used to be setuid-root, now it's given only &lt;code&gt;CAP_NET_RAW&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Look at your shell's capabilities&lt;/span&gt;
capsh &lt;span class="nt"&gt;--print&lt;/span&gt;

&lt;span class="c"&gt;# Look at a file's capabilities&lt;/span&gt;
getcap /usr/bin/ping
&lt;span class="c"&gt;# /usr/bin/ping cap_net_raw=ep&lt;/span&gt;

&lt;span class="c"&gt;# Give Nginx the capability to bind port 80 (a privileged port) without running as root&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;setcap &lt;span class="s1"&gt;'cap_net_bind_service=+ep'&lt;/span&gt; /usr/local/bin/nginx
getcap /usr/local/bin/nginx
&lt;span class="c"&gt;# /usr/local/bin/nginx cap_net_bind_service=ep&lt;/span&gt;

&lt;span class="c"&gt;# Common capabilities&lt;/span&gt;
&lt;span class="c"&gt;# CAP_NET_BIND_SERVICE  - bind ports &amp;lt; 1024&lt;/span&gt;
&lt;span class="c"&gt;# CAP_NET_ADMIN         - network config (ip, iptables)&lt;/span&gt;
&lt;span class="c"&gt;# CAP_SYS_ADMIN         - "almost root" — very powerful, be careful&lt;/span&gt;
&lt;span class="c"&gt;# CAP_CHOWN             - change file ownership&lt;/span&gt;
&lt;span class="c"&gt;# CAP_DAC_OVERRIDE      - bypass permissions&lt;/span&gt;
&lt;span class="c"&gt;# CAP_KILL              - send a signal to any process&lt;/span&gt;
&lt;span class="c"&gt;# CAP_SETUID/SETGID     - change UID/GID&lt;/span&gt;

&lt;span class="c"&gt;# Remove a capability&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;setcap &lt;span class="nt"&gt;-r&lt;/span&gt; /usr/local/bin/nginx

&lt;span class="c"&gt;# Docker drops a lot of caps by default&lt;/span&gt;
docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; alpine sh &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'apk add -q libcap; capsh --print'&lt;/span&gt; | &lt;span class="nb"&gt;grep &lt;/span&gt;Current
&lt;span class="c"&gt;# Current: cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,...&lt;/span&gt;
&lt;span class="c"&gt;# (NOT full root capabilities)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Performance Tuning — sysctl, ulimit, I/O Schedulers
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Kernel Parameters via sysctl
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check the current value&lt;/span&gt;
sysctl vm.swappiness
sysctl net.core.somaxconn

&lt;span class="c"&gt;# Runtime change (gone after reboot)&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;sysctl &lt;span class="nt"&gt;-w&lt;/span&gt; vm.swappiness&lt;span class="o"&gt;=&lt;/span&gt;10
&lt;span class="nb"&gt;sudo &lt;/span&gt;sysctl &lt;span class="nt"&gt;-w&lt;/span&gt; net.core.somaxconn&lt;span class="o"&gt;=&lt;/span&gt;4096

&lt;span class="c"&gt;# Persist it — create a file in /etc/sysctl.d/&lt;/span&gt;
&lt;span class="nb"&gt;sudo tee&lt;/span&gt; /etc/sysctl.d/99-devops-tuning.conf &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
# Memory: prefer dropping cache over swapping (DB/app servers)
vm.swappiness = 10
vm.dirty_ratio = 15
vm.dirty_background_ratio = 5
vm.overcommit_memory = 1

# Network: high-connection web server
net.core.somaxconn = 65535
net.core.netdev_max_backlog = 5000
net.ipv4.tcp_max_syn_backlog = 8192
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 15
net.ipv4.ip_local_port_range = 1024 65535

# File descriptors
fs.file-max = 2097152
fs.inotify.max_user_watches = 524288

# Connection tracking (for nat/firewalls)
net.netfilter.nf_conntrack_max = 1048576
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="c"&gt;# Apply it&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;sysctl &lt;span class="nt"&gt;--system&lt;/span&gt;

&lt;span class="c"&gt;# All current values&lt;/span&gt;
sysctl &lt;span class="nt"&gt;-a&lt;/span&gt; | less
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  ulimit — Per-Process Resource Limits
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Current limits&lt;/span&gt;
&lt;span class="nb"&gt;ulimit&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt;

&lt;span class="c"&gt;# Open files limit (the most common bottleneck)&lt;/span&gt;
&lt;span class="nb"&gt;ulimit&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt;               &lt;span class="c"&gt;# soft&lt;/span&gt;
&lt;span class="nb"&gt;ulimit&lt;/span&gt; &lt;span class="nt"&gt;-Hn&lt;/span&gt;              &lt;span class="c"&gt;# hard&lt;/span&gt;

&lt;span class="c"&gt;# Raise it temporarily (current shell only)&lt;/span&gt;
&lt;span class="nb"&gt;ulimit&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; 65535

&lt;span class="c"&gt;# Permanent — /etc/security/limits.conf or /etc/security/limits.d/&lt;/span&gt;
&lt;span class="nb"&gt;sudo tee&lt;/span&gt; /etc/security/limits.d/99-app.conf &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
# domain  type  item   value
*         soft  nofile 65535
*         hard  nofile 1048576
*         soft  nproc  32768
*         hard  nproc  65535
www-data  soft  nofile 100000
www-data  hard  nofile 200000
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="c"&gt;# For systemd services — in the service unit file&lt;/span&gt;
&lt;span class="c"&gt;# [Service]&lt;/span&gt;
&lt;span class="c"&gt;# LimitNOFILE=1048576&lt;/span&gt;
&lt;span class="c"&gt;# LimitNPROC=65535&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  I/O Schedulers
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Look at the current scheduler&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; /sys/block/sda/queue/scheduler
&lt;span class="c"&gt;# [mq-deadline] kyber bfq none&lt;/span&gt;

&lt;span class="c"&gt;# 'none' is best for NVMe — NVMe has its own queue management&lt;/span&gt;
&lt;span class="nb"&gt;echo &lt;/span&gt;none | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; /sys/block/nvme0n1/queue/scheduler

&lt;span class="c"&gt;# For SATA SSD / HDD use 'mq-deadline' or 'bfq'&lt;/span&gt;
&lt;span class="nb"&gt;echo &lt;/span&gt;mq-deadline | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; /sys/block/sda/queue/scheduler

&lt;span class="c"&gt;# Persist via udev rule&lt;/span&gt;
&lt;span class="nb"&gt;sudo tee&lt;/span&gt; /etc/udev/rules.d/60-ioschedulers.rules &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
# NVMe
ACTION=="add|change", KERNEL=="nvme[0-9]*", ATTR{queue/scheduler}="none"
# SSD
ACTION=="add|change", KERNEL=="sd[a-z]", ATTR{queue/rotational}=="0", ATTR{queue/scheduler}="mq-deadline"
# HDD
ACTION=="add|change", KERNEL=="sd[a-z]", ATTR{queue/rotational}=="1", ATTR{queue/scheduler}="bfq"
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; The most common cause of production outages — &lt;code&gt;ulimit -n&lt;/code&gt; set too low. Under load, Node.js / Java apps crash at 1024 file descriptors with "EMFILE: too many open files". Always set it to 65535+.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Boot Process Deep Dive
&lt;/h3&gt;

&lt;p&gt;Press the power button → kernel running → systemd → login prompt. There are many layers in between:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Stage&lt;/th&gt;
&lt;th&gt;UEFI Boot&lt;/th&gt;
&lt;th&gt;Legacy BIOS&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1. Firmware&lt;/td&gt;
&lt;td&gt;UEFI firmware (modern)&lt;/td&gt;
&lt;td&gt;BIOS (legacy)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2. Boot loader location&lt;/td&gt;
&lt;td&gt;EFI System Partition (ESP), FAT32, mounted at &lt;code&gt;/boot/efi&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;MBR (first 512 bytes of disk)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3. Boot loader&lt;/td&gt;
&lt;td&gt;GRUB EFI binary (&lt;code&gt;/boot/efi/EFI/ubuntu/grubx64.efi&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;GRUB stage 1 → stage 2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4. Config&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;/boot/grub/grub.cfg&lt;/code&gt; (generated)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/boot/grub/grub.cfg&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5. Kernel + initramfs&lt;/td&gt;
&lt;td&gt;Loaded from &lt;code&gt;/boot/&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Loaded from &lt;code&gt;/boot/&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6. Secure Boot&lt;/td&gt;
&lt;td&gt;Supported (shim + signed kernel)&lt;/td&gt;
&lt;td&gt;Not supported&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7. Disk size&lt;/td&gt;
&lt;td&gt;&amp;gt; 2 TB OK (GPT)&lt;/td&gt;
&lt;td&gt;2 TB limit (MBR)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check whether you booted in UEFI mode&lt;/span&gt;
&lt;span class="nb"&gt;ls&lt;/span&gt; /sys/firmware/efi &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"UEFI"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"BIOS"&lt;/span&gt;

&lt;span class="c"&gt;# Look at the ESP partition&lt;/span&gt;
&lt;span class="nb"&gt;df&lt;/span&gt; &lt;span class="nt"&gt;-h&lt;/span&gt; /boot/efi
mount | &lt;span class="nb"&gt;grep&lt;/span&gt; /boot/efi
&lt;span class="c"&gt;# /dev/nvme0n1p1 on /boot/efi type vfat ...&lt;/span&gt;

&lt;span class="c"&gt;# EFI boot entries&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;efibootmgr &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;span class="c"&gt;# BootCurrent: 0001&lt;/span&gt;
&lt;span class="c"&gt;# Boot0001* ubuntu  HD(1,GPT,...)/File(\EFI\ubuntu\shimx64.efi)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  GRUB2 Configuration
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Edit defaults&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;nano /etc/default/grub

&lt;span class="c"&gt;# Common settings:&lt;/span&gt;
&lt;span class="nv"&gt;GRUB_DEFAULT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0
&lt;span class="nv"&gt;GRUB_TIMEOUT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5
&lt;span class="nv"&gt;GRUB_CMDLINE_LINUX_DEFAULT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"quiet splash"&lt;/span&gt;
&lt;span class="nv"&gt;GRUB_CMDLINE_LINUX&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;

&lt;span class="c"&gt;# Add a kernel parameter (example: disable IPv6, enable cgroup v2 — already default in 24.04)&lt;/span&gt;
&lt;span class="nv"&gt;GRUB_CMDLINE_LINUX_DEFAULT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"quiet splash ipv6.disable=1"&lt;/span&gt;

&lt;span class="c"&gt;# Regenerate grub.cfg&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;update-grub
&lt;span class="c"&gt;# OR:  sudo grub-mkconfig -o /boot/grub/grub.cfg&lt;/span&gt;

&lt;span class="c"&gt;# Rebuild initramfs (after a driver change or crypto change)&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;update-initramfs &lt;span class="nt"&gt;-u&lt;/span&gt; &lt;span class="nt"&gt;-k&lt;/span&gt; all

&lt;span class="c"&gt;# Kernel command line currently running&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; /proc/cmdline
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  initramfs (initial RAM filesystem)
&lt;/h4&gt;

&lt;p&gt;Right after the kernel boots, it needs drivers to mount the real root filesystem (NVMe, RAID, LVM, encrypted disk). All of this happens in a temporary RAM-based filesystem — the &lt;code&gt;initramfs&lt;/code&gt;. &lt;code&gt;/boot/initrd.img-*&lt;/code&gt; is a compressed cpio archive.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# See what's inside&lt;/span&gt;
lsinitramfs /boot/initrd.img-&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;uname&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-50&lt;/span&gt;

&lt;span class="c"&gt;# Rebuild&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;update-initramfs &lt;span class="nt"&gt;-u&lt;/span&gt; &lt;span class="nt"&gt;-k&lt;/span&gt; all
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  systemd Boot Targets &amp;amp; Analysis
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Current target&lt;/span&gt;
systemctl get-default          &lt;span class="c"&gt;# graphical.target or multi-user.target&lt;/span&gt;

&lt;span class="c"&gt;# Set it&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl set-default multi-user.target

&lt;span class="c"&gt;# Total boot time for a server&lt;/span&gt;
systemd-analyze
&lt;span class="c"&gt;# Startup finished in 3.124s (kernel) + 8.456s (userspace) = 11.580s&lt;/span&gt;

&lt;span class="c"&gt;# Which service is slow?&lt;/span&gt;
systemd-analyze blame
&lt;span class="c"&gt;# 4.123s snapd.service&lt;/span&gt;
&lt;span class="c"&gt;# 2.456s cloud-init.service&lt;/span&gt;
&lt;span class="c"&gt;# 1.234s apt-daily.service&lt;/span&gt;

&lt;span class="c"&gt;# Critical path (the serial dependency chain of boot)&lt;/span&gt;
systemd-analyze critical-chain
systemd-analyze critical-chain nginx.service

&lt;span class="c"&gt;# Visual SVG plot&lt;/span&gt;
systemd-analyze plot &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /tmp/boot-plot.svg
&lt;span class="c"&gt;# Open it in a browser — you get a beautiful timeline&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  LVM — Logical Volume Manager (Production Essential)
&lt;/h3&gt;

&lt;p&gt;LVM abstracts your disks. You pool multiple physical disks (PVs) into a group (VG), and carve flexible chunks (LVs) out of it — runtime resize, snapshots, and online migration all come for free.&lt;/p&gt;

&lt;p&gt;The LVM stack, top to bottom: &lt;strong&gt;Filesystems&lt;/strong&gt; (&lt;code&gt;/&lt;/code&gt; ext4, &lt;code&gt;/var/lib/docker&lt;/code&gt;, &lt;code&gt;/data&lt;/code&gt; xfs) sit on &lt;strong&gt;LVs&lt;/strong&gt; (lv-root 20G, lv-docker 50G, lv-data 200G), which are carved from the &lt;strong&gt;VG&lt;/strong&gt; (&lt;code&gt;vg_main&lt;/code&gt; — Volume Group, 500 GB total, 230 GB free), which pools the &lt;strong&gt;PVs&lt;/strong&gt; (&lt;code&gt;/dev/nvme0n1p3&lt;/code&gt; 200G, &lt;code&gt;/dev/nvme1n1&lt;/code&gt; 200G, &lt;code&gt;/dev/nvme2n1&lt;/code&gt; 100G — physical disks / partitions / raw block devices).&lt;/p&gt;

&lt;h4&gt;
  
  
  Full End-to-End LVM Example
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Create Physical Volumes (turn raw disks into LVM-managed devices)&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;pvcreate /dev/nvme1n1 /dev/nvme2n1
&lt;span class="nb"&gt;sudo &lt;/span&gt;pvs

&lt;span class="c"&gt;# 2. Create a Volume Group (a pool of PVs)&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;vgcreate vg_main /dev/nvme1n1 /dev/nvme2n1
&lt;span class="nb"&gt;sudo &lt;/span&gt;vgs
&lt;span class="nb"&gt;sudo &lt;/span&gt;vgdisplay vg_main

&lt;span class="c"&gt;# 3. Create Logical Volumes&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;lvcreate &lt;span class="nt"&gt;-L&lt;/span&gt; 50G &lt;span class="nt"&gt;-n&lt;/span&gt; lv-docker vg_main
&lt;span class="nb"&gt;sudo &lt;/span&gt;lvcreate &lt;span class="nt"&gt;-L&lt;/span&gt; 200G &lt;span class="nt"&gt;-n&lt;/span&gt; lv-data vg_main
&lt;span class="c"&gt;# Or all the free space:&lt;/span&gt;
&lt;span class="c"&gt;# sudo lvcreate -l 100%FREE -n lv-data vg_main&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;lvs

&lt;span class="c"&gt;# 4. Create filesystems&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;mkfs.ext4 /dev/vg_main/lv-docker
&lt;span class="nb"&gt;sudo &lt;/span&gt;mkfs.xfs  /dev/vg_main/lv-data

&lt;span class="c"&gt;# 5. Mount them&lt;/span&gt;
&lt;span class="nb"&gt;sudo mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /var/lib/docker /data
&lt;span class="nb"&gt;sudo &lt;/span&gt;mount /dev/vg_main/lv-docker /var/lib/docker
&lt;span class="nb"&gt;sudo &lt;/span&gt;mount /dev/vg_main/lv-data /data

&lt;span class="c"&gt;# 6. Add to /etc/fstab (UUID is best practice)&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"/dev/vg_main/lv-docker /var/lib/docker ext4 defaults,noatime 0 2"&lt;/span&gt; | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; /etc/fstab
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"/dev/vg_main/lv-data   /data           xfs  defaults,noatime 0 2"&lt;/span&gt; | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; /etc/fstab

&lt;span class="c"&gt;# ===== NOW THE LIVE EXTEND — this is the real power of LVM =====&lt;/span&gt;

&lt;span class="c"&gt;# The disk is filling up. Grow lv-data by another 100G&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;lvextend &lt;span class="nt"&gt;-L&lt;/span&gt; +100G /dev/vg_main/lv-data
&lt;span class="nb"&gt;sudo &lt;/span&gt;xfs_growfs /data                    &lt;span class="c"&gt;# XFS online grow&lt;/span&gt;

&lt;span class="c"&gt;# For ext4:&lt;/span&gt;
&lt;span class="c"&gt;# sudo lvextend -L +50G /dev/vg_main/lv-docker&lt;/span&gt;
&lt;span class="c"&gt;# sudo resize2fs /dev/vg_main/lv-docker&lt;/span&gt;

&lt;span class="c"&gt;# Combined (lvextend + filesystem resize in a single command):&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;lvextend &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="nt"&gt;-L&lt;/span&gt; +50G /dev/vg_main/lv-docker

&lt;span class="c"&gt;# Out of space in the VG? Add a new disk&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;pvcreate /dev/nvme3n1
&lt;span class="nb"&gt;sudo &lt;/span&gt;vgextend vg_main /dev/nvme3n1
&lt;span class="nb"&gt;sudo &lt;/span&gt;lvextend &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="nt"&gt;-L&lt;/span&gt; +500G /dev/vg_main/lv-data

&lt;span class="c"&gt;# ===== SNAPSHOTS — for backups =====&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;lvcreate &lt;span class="nt"&gt;-L&lt;/span&gt; 10G &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; lv-data-snap /dev/vg_main/lv-data
&lt;span class="nb"&gt;sudo &lt;/span&gt;mount &lt;span class="nt"&gt;-o&lt;/span&gt; ro /dev/vg_main/lv-data-snap /mnt/snapshot
&lt;span class="nb"&gt;tar &lt;/span&gt;czf /backup/data-&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%F&lt;span class="si"&gt;)&lt;/span&gt;.tar.gz &lt;span class="nt"&gt;-C&lt;/span&gt; /mnt/snapshot &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;umount /mnt/snapshot
&lt;span class="nb"&gt;sudo &lt;/span&gt;lvremove &lt;span class="nt"&gt;-f&lt;/span&gt; /dev/vg_main/lv-data-snap
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Cloud relevance — why LVM on AWS? To stripe or pool multiple EBS volumes. Take 4×500GB gp3 volumes on an instance and make a single 2TB volume with LVM — combined IOPS goes up and growing it is easy.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  RAID — Redundancy and Performance
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;RAID Level&lt;/th&gt;
&lt;th&gt;Min Disks&lt;/th&gt;
&lt;th&gt;Redundancy&lt;/th&gt;
&lt;th&gt;Capacity&lt;/th&gt;
&lt;th&gt;Read/Write&lt;/th&gt;
&lt;th&gt;Use Case&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;RAID 0 (stripe)&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;None — 1 fail = data gone&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;Fastest R + W&lt;/td&gt;
&lt;td&gt;Scratch / cache (cloud: avoid)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RAID 1 (mirror)&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;1 disk fail OK&lt;/td&gt;
&lt;td&gt;50%&lt;/td&gt;
&lt;td&gt;Fast R, normal W&lt;/td&gt;
&lt;td&gt;OS disk, boot&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RAID 5 (parity)&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;1 disk fail OK&lt;/td&gt;
&lt;td&gt;(N-1)/N&lt;/td&gt;
&lt;td&gt;Good R, slow W (parity calc)&lt;/td&gt;
&lt;td&gt;Bulk storage (legacy)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RAID 6 (double parity)&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;2 disks fail OK&lt;/td&gt;
&lt;td&gt;(N-2)/N&lt;/td&gt;
&lt;td&gt;Good R, slower W&lt;/td&gt;
&lt;td&gt;Large arrays, long rebuilds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RAID 10 (1+0)&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;1 per mirror pair OK&lt;/td&gt;
&lt;td&gt;50%&lt;/td&gt;
&lt;td&gt;Fastest R + W with redundancy&lt;/td&gt;
&lt;td&gt;Databases, production&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Software RAID with mdadm&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;mdadm

&lt;span class="c"&gt;# RAID 10 from 4 disks&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;mdadm &lt;span class="nt"&gt;--create&lt;/span&gt; /dev/md0 &lt;span class="nt"&gt;--level&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;10 &lt;span class="nt"&gt;--raid-devices&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;4 &lt;span class="se"&gt;\&lt;/span&gt;
  /dev/nvme1n1 /dev/nvme2n1 /dev/nvme3n1 /dev/nvme4n1

&lt;span class="c"&gt;# Status&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; /proc/mdstat
&lt;span class="nb"&gt;sudo &lt;/span&gt;mdadm &lt;span class="nt"&gt;--detail&lt;/span&gt; /dev/md0

&lt;span class="c"&gt;# Save config (otherwise it disappears on boot)&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;mdadm &lt;span class="nt"&gt;--detail&lt;/span&gt; &lt;span class="nt"&gt;--scan&lt;/span&gt; | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; /etc/mdadm/mdadm.conf
&lt;span class="nb"&gt;sudo &lt;/span&gt;update-initramfs &lt;span class="nt"&gt;-u&lt;/span&gt;

&lt;span class="c"&gt;# Use as an LVM PV or a direct filesystem&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;mkfs.xfs /dev/md0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; Cloud reality — AWS EBS / GCP PD / Azure Managed Disks are already replicated (3 copies cross-AZ). You rarely run RAID in the cloud — the provider already does it. Just attach multiple EBS volumes + LVM stripe for IOPS — that's the cloud pattern. mdadm is only critical on bare-metal / on-prem.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Filesystem Tuning
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# ext4: lower reserved blocks (root-only) from 5% to 1% — save space on big data disks&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;tune2fs &lt;span class="nt"&gt;-m&lt;/span&gt; 1 /dev/vg_main/lv-data

&lt;span class="c"&gt;# Turn off forced fsck interval / mount count (production servers — avoid fsck on boot)&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;tune2fs &lt;span class="nt"&gt;-c&lt;/span&gt; 0 &lt;span class="nt"&gt;-i&lt;/span&gt; 0 /dev/vg_main/lv-data

&lt;span class="c"&gt;# Show all options&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;tune2fs &lt;span class="nt"&gt;-l&lt;/span&gt; /dev/vg_main/lv-data

&lt;span class="c"&gt;# Mount options that matter in production&lt;/span&gt;
&lt;span class="c"&gt;# noatime    — don't update atime on every read (huge perf gain, esp. for DBs, web)&lt;/span&gt;
&lt;span class="c"&gt;# nodiratime — skip directory atime&lt;/span&gt;
&lt;span class="c"&gt;# discard    — SSD TRIM on delete (alternative: fstrim weekly via timer)&lt;/span&gt;
&lt;span class="c"&gt;# nofail     — don't fail boot if the disk is missing (cloud detached EBS scenario)&lt;/span&gt;
&lt;span class="c"&gt;# nosuid,nodev,noexec — security for /tmp, /var&lt;/span&gt;

&lt;span class="c"&gt;# /etc/fstab example for production&lt;/span&gt;
&lt;span class="c"&gt;# UUID=...  /data       ext4  defaults,noatime,nodiratime,nofail  0  2&lt;/span&gt;
&lt;span class="c"&gt;# UUID=...  /tmp        tmpfs defaults,nosuid,nodev,noexec,size=2G 0  0&lt;/span&gt;

&lt;span class="c"&gt;# tmpfs (RAM-backed) in production — fast scratch / build directories&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;mount &lt;span class="nt"&gt;-t&lt;/span&gt; tmpfs &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;4G,mode&lt;span class="o"&gt;=&lt;/span&gt;1777 tmpfs /var/cache/build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Quick recap — A Docker container = unshare + cgroups + overlayfs + capability drop. Boot = UEFI → GRUB → kernel → initramfs → systemd. LVM = flexible disks, runtime grow. RAID = redundancy. You rarely need RAID in the cloud, but LVM and tuning are useful everywhere. All of these show up in interviews as the answer to "how does X work internally?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Linux in Cloud Context
&lt;/h2&gt;

&lt;p&gt;So far we've looked at Linux on a single isolated machine. In the cloud the same Linux is running, but with different patterns — ephemeral instances, the metadata service, cloud-init bootstrapping, EBS attach/grow, replacing SSH with SSM. A developer-turned-DevOps engineer spends roughly 70% of each day on AWS Linux instances + Kubernetes nodes. This section covers that 70%.&lt;/p&gt;

&lt;h3&gt;
  
  
  AWS EC2 Linux Distributions
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Distro&lt;/th&gt;
&lt;th&gt;Default User&lt;/th&gt;
&lt;th&gt;Package Mgr&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Amazon Linux 2023 (AL2023)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ec2-user&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;dnf&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;AWS optimized, kernel 6.x, glibc 2.34, free, fast-boot. Default choice for AWS.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Amazon Linux 2 (legacy)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ec2-user&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;yum&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;EOL 2026. Migrate to AL2023.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ubuntu Server 24.04 LTS&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ubuntu&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;apt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Most popular, huge community, debs, snap. Best for general use.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rocky Linux 9 / RHEL 9&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;rocky&lt;/code&gt; / &lt;code&gt;ec2-user&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;dnf&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Enterprise, RHEL-compatible, FIPS, certifications.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Debian 12&lt;/td&gt;
&lt;td&gt;&lt;code&gt;admin&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;apt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Minimal, stable, like Ubuntu's parent.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Custom AMI&lt;/td&gt;
&lt;td&gt;Whatever you bake&lt;/td&gt;
&lt;td&gt;Whatever&lt;/td&gt;
&lt;td&gt;Pre-configured golden image via Packer.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Practical pick — Production Kubernetes nodes → Amazon Linux 2023 (also consider Bottlerocket). General app servers → Ubuntu 24.04. Enterprise compliance → RHEL/Rocky.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Instance Metadata Service (IMDS) — The Cloud's /proc
&lt;/h3&gt;

&lt;p&gt;Every EC2 instance gets a special endpoint: &lt;code&gt;http://169.254.169.254&lt;/code&gt;. From here the instance can learn everything about itself — region, AZ, instance type, IAM role credentials, user-data, public IP. SDKs (boto3, aws-cli) also pull their IAM creds from here.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# IMDSv1 (legacy, insecure — SSRF attacks possible)&lt;/span&gt;
curl http://169.254.169.254/latest/meta-data/

&lt;span class="c"&gt;# IMDSv2 (token-based, mandatory in new accounts) — SECURE PATTERN&lt;/span&gt;
&lt;span class="nv"&gt;TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; PUT &lt;span class="s2"&gt;"http://169.254.169.254/latest/api/token"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-aws-ec2-metadata-token-ttl-seconds: 21600"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Use token in all requests&lt;/span&gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-aws-ec2-metadata-token: &lt;/span&gt;&lt;span class="nv"&gt;$TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  http://169.254.169.254/latest/meta-data/

&lt;span class="c"&gt;# Useful endpoints&lt;/span&gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-aws-ec2-metadata-token: &lt;/span&gt;&lt;span class="nv"&gt;$TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; http://169.254.169.254/latest/meta-data/instance-id
curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-aws-ec2-metadata-token: &lt;/span&gt;&lt;span class="nv"&gt;$TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; http://169.254.169.254/latest/meta-data/instance-type
curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-aws-ec2-metadata-token: &lt;/span&gt;&lt;span class="nv"&gt;$TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; http://169.254.169.254/latest/meta-data/placement/availability-zone
curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-aws-ec2-metadata-token: &lt;/span&gt;&lt;span class="nv"&gt;$TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; http://169.254.169.254/latest/meta-data/placement/region
curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-aws-ec2-metadata-token: &lt;/span&gt;&lt;span class="nv"&gt;$TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; http://169.254.169.254/latest/meta-data/local-ipv4
curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-aws-ec2-metadata-token: &lt;/span&gt;&lt;span class="nv"&gt;$TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; http://169.254.169.254/latest/meta-data/public-ipv4
curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-aws-ec2-metadata-token: &lt;/span&gt;&lt;span class="nv"&gt;$TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; http://169.254.169.254/latest/meta-data/security-groups
curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-aws-ec2-metadata-token: &lt;/span&gt;&lt;span class="nv"&gt;$TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; http://169.254.169.254/latest/meta-data/iam/security-credentials/

&lt;span class="c"&gt;# Tags (if enabled on instance)&lt;/span&gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-aws-ec2-metadata-token: &lt;/span&gt;&lt;span class="nv"&gt;$TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; http://169.254.169.254/latest/meta-data/tags/instance/

&lt;span class="c"&gt;# User-data (the bootstrap script)&lt;/span&gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-aws-ec2-metadata-token: &lt;/span&gt;&lt;span class="nv"&gt;$TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; http://169.254.169.254/latest/user-data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; IMDSv1 is vulnerable to SSRF attacks — the Capital One breach happened through it. Always enforce IMDSv2 (HttpTokens=required) in the instance metadata options.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  EBS Volumes — Attach, Format, Mount, Grow
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# After attaching a new volume, look at the device list&lt;/span&gt;
lsblk
&lt;span class="c"&gt;# NAME         SIZE TYPE MOUNTPOINTS&lt;/span&gt;
&lt;span class="c"&gt;# nvme0n1       30G disk&lt;/span&gt;
&lt;span class="c"&gt;# `-nvme0n1p1   30G part /&lt;/span&gt;
&lt;span class="c"&gt;# nvme1n1      100G disk                  &amp;lt;-- new EBS volume&lt;/span&gt;

&lt;span class="c"&gt;# No filesystem on it yet&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;file &lt;span class="nt"&gt;-s&lt;/span&gt; /dev/nvme1n1
&lt;span class="c"&gt;# /dev/nvme1n1: data        (i.e. empty)&lt;/span&gt;

&lt;span class="c"&gt;# Create a filesystem&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;mkfs.ext4 /dev/nvme1n1
&lt;span class="c"&gt;# Modern alternative: sudo mkfs.xfs /dev/nvme1n1&lt;/span&gt;

&lt;span class="c"&gt;# Create a mountpoint and mount it&lt;/span&gt;
&lt;span class="nb"&gt;sudo mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /data
&lt;span class="nb"&gt;sudo &lt;/span&gt;mount /dev/nvme1n1 /data

&lt;span class="c"&gt;# /etc/fstab — UUID best practice; nofail critical for cloud&lt;/span&gt;
&lt;span class="c"&gt;# (if the instance reboots with the volume detached, boot won't fail)&lt;/span&gt;
&lt;span class="nv"&gt;UUID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;blkid &lt;span class="nt"&gt;-s&lt;/span&gt; UUID &lt;span class="nt"&gt;-o&lt;/span&gt; value /dev/nvme1n1&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"UUID=&lt;/span&gt;&lt;span class="nv"&gt;$UUID&lt;/span&gt;&lt;span class="s2"&gt;  /data  ext4  defaults,nofail,noatime  0  2"&lt;/span&gt; | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; /etc/fstab

&lt;span class="c"&gt;# Verify&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;mount &lt;span class="nt"&gt;-a&lt;/span&gt;
&lt;span class="nb"&gt;df&lt;/span&gt; &lt;span class="nt"&gt;-h&lt;/span&gt; /data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Grow EBS Volume In-Place (Zero Downtime)
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Step 1: Increase the volume size from the AWS console or CLI&lt;/span&gt;
aws ec2 modify-volume &lt;span class="nt"&gt;--volume-id&lt;/span&gt; vol-0abc123 &lt;span class="nt"&gt;--size&lt;/span&gt; 200

&lt;span class="c"&gt;# Wait for "optimizing" state&lt;/span&gt;
aws ec2 describe-volumes-modifications &lt;span class="nt"&gt;--volume-id&lt;/span&gt; vol-0abc123

&lt;span class="c"&gt;# Step 2: Inside the instance, the kernel sees the new size&lt;/span&gt;
lsblk
&lt;span class="c"&gt;# nvme1n1      200G disk&lt;/span&gt;
&lt;span class="c"&gt;# `-nvme1n1p1  100G part /data         &amp;lt;-- partition is still 100G&lt;/span&gt;

&lt;span class="c"&gt;# Step 3: Grow the partition (if you're using a partition)&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;growpart /dev/nvme1n1 1

&lt;span class="c"&gt;# Filesystem directly on the raw device (no partition) — skip this step&lt;/span&gt;

&lt;span class="c"&gt;# Step 4: Grow the filesystem&lt;/span&gt;
&lt;span class="c"&gt;# ext4:&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;resize2fs /dev/nvme1n1p1
&lt;span class="c"&gt;# OR direct:&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;resize2fs /dev/nvme1n1

&lt;span class="c"&gt;# xfs (must be mounted):&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;xfs_growfs /data

&lt;span class="c"&gt;# Verify&lt;/span&gt;
&lt;span class="nb"&gt;df&lt;/span&gt; &lt;span class="nt"&gt;-h&lt;/span&gt; /data
&lt;span class="c"&gt;# /dev/nvme1n1   200G ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Instance Store vs EBS
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;EBS&lt;/th&gt;
&lt;th&gt;Instance Store&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Persistence&lt;/td&gt;
&lt;td&gt;Persistent — survives stop/start&lt;/td&gt;
&lt;td&gt;Ephemeral — gone on stop/terminate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Replication&lt;/td&gt;
&lt;td&gt;3× within AZ&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Speed&lt;/td&gt;
&lt;td&gt;Network-attached (NVMe over fabric)&lt;/td&gt;
&lt;td&gt;Physically attached NVMe SSD — fastest&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Snapshots&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Detach &amp;amp; reattach&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Use case&lt;/td&gt;
&lt;td&gt;OS root, databases, app data&lt;/td&gt;
&lt;td&gt;Cache, scratch, temp big-data shuffle&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  SSH to Cloud Instances
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Classic: keypair-based SSH&lt;/span&gt;
&lt;span class="nb"&gt;chmod &lt;/span&gt;400 ~/.ssh/my-key.pem
ssh &lt;span class="nt"&gt;-i&lt;/span&gt; ~/.ssh/my-key.pem ec2-user@54.221.10.45    &lt;span class="c"&gt;# Amazon Linux&lt;/span&gt;
ssh &lt;span class="nt"&gt;-i&lt;/span&gt; ~/.ssh/my-key.pem ubuntu@54.221.10.45      &lt;span class="c"&gt;# Ubuntu&lt;/span&gt;

&lt;span class="c"&gt;# A convenient alias in ~/.ssh/config&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; ~/.ssh/config &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
Host prod-web
  HostName 54.221.10.45
  User ubuntu
  IdentityFile ~/.ssh/my-key.pem
  ServerAliveInterval 60
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;ssh prod-web

&lt;span class="c"&gt;# Bastion host pattern (private instance via public jump box)&lt;/span&gt;
ssh &lt;span class="nt"&gt;-i&lt;/span&gt; key.pem &lt;span class="nt"&gt;-J&lt;/span&gt; bastion.example.com ubuntu@10.0.5.20
&lt;span class="c"&gt;# Or ProxyJump in your config&lt;/span&gt;
Host prod-db
  HostName 10.0.5.20
  User ubuntu
  IdentityFile ~/.ssh/my-key.pem
  ProxyJump bastion.example.com

&lt;span class="c"&gt;# ===== MODERN: SSM Session Manager (NO PORT 22, NO KEYS) =====&lt;/span&gt;
&lt;span class="c"&gt;# Pre-requisites: SSM agent on instance, IAM role with AmazonSSMManagedInstanceCore&lt;/span&gt;

aws ssm start-session &lt;span class="nt"&gt;--target&lt;/span&gt; i-0abc123def456

&lt;span class="c"&gt;# Port forwarding via SSM (RDS access through private instance)&lt;/span&gt;
aws ssm start-session &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--target&lt;/span&gt; i-0abc123def456 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--document-name&lt;/span&gt; AWS-StartPortForwardingSessionToRemoteHost &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--parameters&lt;/span&gt; &lt;span class="s1"&gt;'{"host":["mydb.cluster-xyz.rds.amazonaws.com"],"portNumber":["5432"],"localPortNumber":["15432"]}'&lt;/span&gt;

&lt;span class="c"&gt;# SSH over SSM (best of both)&lt;/span&gt;
&lt;span class="c"&gt;# ~/.ssh/config:&lt;/span&gt;
Host i-&lt;span class="k"&gt;*&lt;/span&gt; mi-&lt;span class="k"&gt;*&lt;/span&gt;
  ProxyCommand sh &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"aws ssm start-session --target %h --document-name AWS-StartSSHSession --parameters portNumber=%p"&lt;/span&gt;

ssh ec2-user@i-0abc123def456
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Production gold-standard — never open port 22 in your security groups. Use SSM Session Manager — no public IP, no SSH key management, full audit log in CloudTrail, IAM-based access control. This is the modern DevOps practice.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  cloud-init — First Boot Bootstrap
&lt;/h3&gt;

&lt;p&gt;cloud-init is Linux's standard first-boot configuration tool. It runs on AWS, GCP, Azure, OpenStack — everywhere. The user-data field you provide at launch time is parsed and executed by cloud-init.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Status check&lt;/span&gt;
cloud-init status
&lt;span class="c"&gt;# status: done&lt;/span&gt;

cloud-init status &lt;span class="nt"&gt;--long&lt;/span&gt;
cloud-init analyze show              &lt;span class="c"&gt;# boot time breakdown&lt;/span&gt;

&lt;span class="c"&gt;# Config files&lt;/span&gt;
&lt;span class="nb"&gt;ls&lt;/span&gt; /etc/cloud/
&lt;span class="c"&gt;# cloud.cfg               main config&lt;/span&gt;
&lt;span class="c"&gt;# cloud.cfg.d/            drop-ins (vendor + user)&lt;/span&gt;
&lt;span class="c"&gt;# templates/              hostname etc.&lt;/span&gt;

&lt;span class="c"&gt;# Logs (THE MOST IMPORTANT thing for debugging)&lt;/span&gt;
&lt;span class="nb"&gt;sudo tail&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; /var/log/cloud-init.log              &lt;span class="c"&gt;# detailed&lt;/span&gt;
&lt;span class="nb"&gt;sudo tail&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; /var/log/cloud-init-output.log       &lt;span class="c"&gt;# stdout/stderr of user-data&lt;/span&gt;

&lt;span class="c"&gt;# Re-run cloud-init (for testing)&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;cloud-init clean &lt;span class="nt"&gt;--logs&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;cloud-init init
&lt;span class="nb"&gt;sudo &lt;/span&gt;cloud-init modules &lt;span class="nt"&gt;--mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;config
&lt;span class="nb"&gt;sudo &lt;/span&gt;cloud-init modules &lt;span class="nt"&gt;--mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;final
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Format 1: Bash Script User-Data
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="c"&gt;# user-data.sh — simple bash, runs as root on first boot&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-euxo&lt;/span&gt; pipefail
&lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="nb"&gt;tee&lt;/span&gt; /var/log/user-data.log | logger &lt;span class="nt"&gt;-t&lt;/span&gt; user-data &lt;span class="nt"&gt;-s&lt;/span&gt; 2&amp;gt;/dev/console&lt;span class="o"&gt;)&lt;/span&gt; 2&amp;gt;&amp;amp;1

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"=== Starting bootstrap at &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; ==="&lt;/span&gt;

&lt;span class="c"&gt;# Idempotency guard&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; /var/lib/bootstrap-done &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Already bootstrapped, exiting"&lt;/span&gt;
  &lt;span class="nb"&gt;exit &lt;/span&gt;0
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# Update + install&lt;/span&gt;
apt-get update &lt;span class="nt"&gt;-y&lt;/span&gt;
apt-get upgrade &lt;span class="nt"&gt;-y&lt;/span&gt;
apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; nginx awscli docker.io curl jq

&lt;span class="c"&gt;# Enable services&lt;/span&gt;
systemctl &lt;span class="nb"&gt;enable&lt;/span&gt; &lt;span class="nt"&gt;--now&lt;/span&gt; nginx docker
usermod &lt;span class="nt"&gt;-aG&lt;/span&gt; docker ubuntu

&lt;span class="c"&gt;# Pull config from S3 (IAM role attached)&lt;/span&gt;
aws s3 &lt;span class="nb"&gt;cp &lt;/span&gt;s3://my-config-bucket/nginx.conf /etc/nginx/nginx.conf
systemctl reload nginx

&lt;span class="c"&gt;# Mark done&lt;/span&gt;
&lt;span class="nb"&gt;touch&lt;/span&gt; /var/lib/bootstrap-done
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"=== Bootstrap complete at &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; ==="&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Format 2: #cloud-config YAML (Recommended)
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#cloud-config&lt;/span&gt;
&lt;span class="c"&gt;# Declarative — much cleaner than bash&lt;/span&gt;

&lt;span class="nb"&gt;hostname&lt;/span&gt;: web-prod-01
fqdn: web-prod-01.internal.example.com
manage_etc_hosts: &lt;span class="nb"&gt;true&lt;/span&gt;

&lt;span class="c"&gt;# Users&lt;/span&gt;
&lt;span class="nb"&gt;users&lt;/span&gt;:
  - name: deploy
    &lt;span class="nb"&gt;groups&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;docker, &lt;span class="nb"&gt;sudo&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
    shell: /bin/bash
    &lt;span class="nb"&gt;sudo&lt;/span&gt;: &lt;span class="s1"&gt;'ALL=(ALL) NOPASSWD:ALL'&lt;/span&gt;
    ssh_authorized_keys:
      - ssh-ed25519 AAAAC3NzaC1lZDI1... deploy@laptop

&lt;span class="c"&gt;# Package install&lt;/span&gt;
package_update: &lt;span class="nb"&gt;true
&lt;/span&gt;package_upgrade: &lt;span class="nb"&gt;true
&lt;/span&gt;packages:
  - nginx
  - docker.io
  - awscli
  - jq
  - htop
  - curl

&lt;span class="c"&gt;# Drop config files&lt;/span&gt;
write_files:
  - path: /etc/nginx/sites-available/default
    permissions: &lt;span class="s1"&gt;'0644'&lt;/span&gt;
    content: |
      server &lt;span class="o"&gt;{&lt;/span&gt;
        listen 80 default_server&lt;span class="p"&gt;;&lt;/span&gt;
        root /var/www/html&lt;span class="p"&gt;;&lt;/span&gt;
        index index.html&lt;span class="p"&gt;;&lt;/span&gt;
        location /health &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return &lt;/span&gt;200 &lt;span class="s1"&gt;'ok'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; add_header Content-Type text/plain&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
      &lt;span class="o"&gt;}&lt;/span&gt;
  - path: /etc/sysctl.d/99-tuning.conf
    content: |
      vm.swappiness &lt;span class="o"&gt;=&lt;/span&gt; 10
      net.core.somaxconn &lt;span class="o"&gt;=&lt;/span&gt; 65535
      fs.file-max &lt;span class="o"&gt;=&lt;/span&gt; 2097152

&lt;span class="c"&gt;# Run commands at end (after packages, files)&lt;/span&gt;
runcmd:
  - sysctl &lt;span class="nt"&gt;--system&lt;/span&gt;
  - systemctl &lt;span class="nb"&gt;enable&lt;/span&gt; &lt;span class="nt"&gt;--now&lt;/span&gt; docker nginx
  - usermod &lt;span class="nt"&gt;-aG&lt;/span&gt; docker ubuntu
  - docker pull nginx:alpine
  - &lt;span class="o"&gt;[&lt;/span&gt; sh, &lt;span class="nt"&gt;-c&lt;/span&gt;, &lt;span class="s1"&gt;'echo "Bootstrap done at $(date)" &amp;gt;&amp;gt; /var/log/bootstrap.log'&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;

&lt;span class="c"&gt;# Reboot if kernel updated (optional)&lt;/span&gt;
power_state:
  mode: reboot
  condition: &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; /var/run/reboot-required
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  AMI Creation — Golden Images
&lt;/h3&gt;

&lt;p&gt;In production, instead of doing a full user-data setup on every deploy, you use pre-baked AMIs — boot in 30 seconds instead of 5 minutes. But before creating an AMI, a "sysprep" (clean) is essential.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Pre-AMI cleanup script (run before creating image)&lt;/span&gt;
&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;

&lt;span class="c"&gt;# Stop services that hold state&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl stop docker nginx

&lt;span class="c"&gt;# Remove SSH host keys (they'll regenerate on the instance's first boot)&lt;/span&gt;
&lt;span class="nb"&gt;sudo rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; /etc/ssh/ssh_host_&lt;span class="k"&gt;*&lt;/span&gt;

&lt;span class="c"&gt;# Clear machine-id (cloud-init will regenerate)&lt;/span&gt;
&lt;span class="nb"&gt;sudo truncate&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; 0 /etc/machine-id
&lt;span class="nb"&gt;sudo rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; /var/lib/dbus/machine-id
&lt;span class="nb"&gt;sudo ln&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; /etc/machine-id /var/lib/dbus/machine-id

&lt;span class="c"&gt;# Clear cloud-init state&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;cloud-init clean &lt;span class="nt"&gt;--logs&lt;/span&gt; &lt;span class="nt"&gt;--seed&lt;/span&gt;

&lt;span class="c"&gt;# Clear logs&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;find /var/log &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="nt"&gt;-exec&lt;/span&gt; &lt;span class="nb"&gt;truncate&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; 0 &lt;span class="o"&gt;{}&lt;/span&gt; &lt;span class="se"&gt;\;&lt;/span&gt;
&lt;span class="nb"&gt;sudo rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /var/log/journal/&lt;span class="k"&gt;*&lt;/span&gt;

&lt;span class="c"&gt;# Clear bash history&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; /dev/null &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; ~/.bash_history
&lt;span class="nb"&gt;history&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt;
&lt;span class="nb"&gt;sudo rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; /root/.bash_history

&lt;span class="c"&gt;# Clear authorized_keys for default user (cloud-init re-injects)&lt;/span&gt;
&lt;span class="nb"&gt;sudo rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; /home/ubuntu/.ssh/authorized_keys

&lt;span class="c"&gt;# Trim filesystem (smaller snapshot)&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;fstrim &lt;span class="nt"&gt;-av&lt;/span&gt;

&lt;span class="c"&gt;# Now from your laptop/CI:&lt;/span&gt;
aws ec2 create-image &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--instance-id&lt;/span&gt; i-0abc123 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="s2"&gt;"web-base-&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%Y%m%d-%H%M&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--description&lt;/span&gt; &lt;span class="s2"&gt;"Nginx + Docker baseline"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--no-reboot&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;

&lt;span class="c"&gt;# Packer (recommended for repeatable builds) — basic config&lt;/span&gt;
&lt;span class="c"&gt;# packer build webserver.pkr.hcl&lt;/span&gt;
&lt;span class="c"&gt;# Packer launches temp instance, runs provisioners, creates AMI, terminates.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Provider Differences
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;AWS EC2&lt;/th&gt;
&lt;th&gt;GCP Compute Engine&lt;/th&gt;
&lt;th&gt;Azure VM&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Metadata endpoint&lt;/td&gt;
&lt;td&gt;&lt;code&gt;169.254.169.254&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;metadata.google.internal&lt;/code&gt; / &lt;code&gt;169.254.169.254&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;169.254.169.254&lt;/code&gt; (with header)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Default users&lt;/td&gt;
&lt;td&gt;ec2-user, ubuntu, rocky, admin&lt;/td&gt;
&lt;td&gt;username from SSH key (gcloud creates one)&lt;/td&gt;
&lt;td&gt;azureuser (you pick at create)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Agent&lt;/td&gt;
&lt;td&gt;SSM Agent + ec2-instance-connect&lt;/td&gt;
&lt;td&gt;Google guest agent / OS Login&lt;/td&gt;
&lt;td&gt;WALinuxAgent (waagent)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Agent config&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/etc/amazon/ssm/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/etc/default/instance_configs.cfg&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/etc/waagent.conf&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Block storage&lt;/td&gt;
&lt;td&gt;EBS&lt;/td&gt;
&lt;td&gt;Persistent Disk (PD)&lt;/td&gt;
&lt;td&gt;Managed Disks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bootstrap&lt;/td&gt;
&lt;td&gt;user-data + cloud-init&lt;/td&gt;
&lt;td&gt;startup-script (metadata) + cloud-init&lt;/td&gt;
&lt;td&gt;customData / cloud-init&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Keyless SSH&lt;/td&gt;
&lt;td&gt;SSM Session Manager&lt;/td&gt;
&lt;td&gt;OS Login + IAP tunnel&lt;/td&gt;
&lt;td&gt;Bastion / Just-In-Time&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# GCP metadata example&lt;/span&gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"http://metadata.google.internal/computeMetadata/v1/instance/name"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Metadata-Flavor: Google"&lt;/span&gt;

&lt;span class="c"&gt;# Azure metadata example (note required header)&lt;/span&gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Metadata:true"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"http://169.254.169.254/metadata/instance?api-version=2021-02-01"&lt;/span&gt; | jq
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Real-World DevOps Patterns
&lt;/h3&gt;

&lt;h4&gt;
  
  
  1. Immutable Infrastructure
&lt;/h4&gt;

&lt;p&gt;Don't patch the server — launch a new instance from a new AMI, shift traffic, terminate the old one. Configuration drift = zero. Rollback = re-launch the previous AMI. This is the modern way.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. cloud-init + Configuration Management Combo
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#cloud-config&lt;/span&gt;
&lt;span class="c"&gt;# cloud-init only "bootstraps Ansible/Chef" — that tool does the rest&lt;/span&gt;
package_update: &lt;span class="nb"&gt;true
&lt;/span&gt;packages: &lt;span class="o"&gt;[&lt;/span&gt;ansible, git]
runcmd:
  - git clone https://github.com/myorg/ansible-playbooks.git /opt/ansible
  - ansible-pull &lt;span class="nt"&gt;-U&lt;/span&gt; https://github.com/myorg/ansible-playbooks.git &lt;span class="nt"&gt;-i&lt;/span&gt; localhost, site.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  3. Centralized Logging
&lt;/h4&gt;

&lt;p&gt;Local logs on an instance (&lt;code&gt;/var/log/*&lt;/code&gt;) are ephemeral — when the instance is terminated, the logs are gone. In production, always ship your logs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# CloudWatch agent&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;amazon-cloudwatch-agent-ctl &lt;span class="nt"&gt;-a&lt;/span&gt; fetch-config &lt;span class="nt"&gt;-m&lt;/span&gt; ec2 &lt;span class="nt"&gt;-c&lt;/span&gt; file:cw-config.json &lt;span class="nt"&gt;-s&lt;/span&gt;

&lt;span class="c"&gt;# Datadog agent&lt;/span&gt;
&lt;span class="nv"&gt;DD_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;xxx &lt;span class="nv"&gt;DD_SITE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"datadoghq.com"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  bash &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-L&lt;/span&gt; https://install.datadoghq.com/scripts/install_script_agent7.sh&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# Promtail (Grafana Loki)&lt;/span&gt;
&lt;span class="c"&gt;# /etc/promtail/config.yml → ships journald + /var/log/* to Loki&lt;/span&gt;

&lt;span class="c"&gt;# rsyslog → centralized syslog server (old-school but reliable)&lt;/span&gt;
&lt;span class="c"&gt;# /etc/rsyslog.d/50-remote.conf:&lt;/span&gt;
&lt;span class="c"&gt;# *.* @@logserver.internal:514&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  4. EBS Snapshots — Automated Backup
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# AWS Backup or Data Lifecycle Manager (DLM) — preferred&lt;/span&gt;
&lt;span class="c"&gt;# Snapshot daily, retain 7 days, cross-region copy weekly&lt;/span&gt;

&lt;span class="c"&gt;# Manual snapshot (one-off)&lt;/span&gt;
aws ec2 create-snapshot &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume-id&lt;/span&gt; vol-0abc123 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--description&lt;/span&gt; &lt;span class="s2"&gt;"pre-migration backup &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%F&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--tag-specifications&lt;/span&gt; &lt;span class="s1"&gt;'ResourceType=snapshot,Tags=[{Key=Backup,Value=manual}]'&lt;/span&gt;

&lt;span class="c"&gt;# Restore: snapshot → new volume → attach to instance&lt;/span&gt;
aws ec2 create-volume &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--snapshot-id&lt;/span&gt; snap-0xyz &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--availability-zone&lt;/span&gt; us-east-1a &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume-type&lt;/span&gt; gp3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  5. SSH via SSM — No Port 22 Open
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Security group inbound rules: (none — yes, ZERO)&lt;/span&gt;
&lt;span class="c"&gt;# Instances are in private subnet&lt;/span&gt;
&lt;span class="c"&gt;# Access only via:&lt;/span&gt;
aws ssm start-session &lt;span class="nt"&gt;--target&lt;/span&gt; i-0abc123

&lt;span class="c"&gt;# And if you need the SSH protocol (scp, rsync, git):&lt;/span&gt;
ssh ec2-user@i-0abc123    &lt;span class="c"&gt;# ProxyCommand via SSM (config shown earlier)&lt;/span&gt;
scp &lt;span class="nt"&gt;-r&lt;/span&gt; ./code/ ec2-user@i-0abc123:/home/ec2-user/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  6. Putting It All Together — A Realistic Stack
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Terraform spins up:&lt;/span&gt;
&lt;span class="c"&gt;#   - VPC with private subnets&lt;/span&gt;
&lt;span class="c"&gt;#   - Auto Scaling Group with Launch Template&lt;/span&gt;
&lt;span class="c"&gt;#   - Launch Template references golden AMI (built by Packer)&lt;/span&gt;
&lt;span class="c"&gt;#   - user-data does minimal "ansible-pull" for last-mile config&lt;/span&gt;
&lt;span class="c"&gt;#   - IAM role attached with SSM + CloudWatch + S3 read&lt;/span&gt;
&lt;span class="c"&gt;#   - Application Load Balancer in public subnet&lt;/span&gt;
&lt;span class="c"&gt;#   - Security groups: ALB → instances on app port only, port 22 NEVER open&lt;/span&gt;
&lt;span class="c"&gt;#   - EBS volumes with daily DLM snapshots&lt;/span&gt;
&lt;span class="c"&gt;#   - CloudWatch agent ships /var/log + custom metrics&lt;/span&gt;
&lt;span class="c"&gt;#   - Logs to CloudWatch Logs → subscription filter → Datadog/Splunk&lt;/span&gt;

&lt;span class="c"&gt;# Deploys happen by:&lt;/span&gt;
&lt;span class="c"&gt;#   1. Packer builds new AMI on git push&lt;/span&gt;
&lt;span class="c"&gt;#   2. Terraform updates Launch Template's AMI ID&lt;/span&gt;
&lt;span class="c"&gt;#   3. ASG instance refresh rolls instances one-by-one&lt;/span&gt;
&lt;span class="c"&gt;#   4. Old AMI kept for 7 days (instant rollback)&lt;/span&gt;

&lt;span class="c"&gt;# Day-2 ops:&lt;/span&gt;
&lt;span class="c"&gt;#   - SSH via `aws ssm start-session`&lt;/span&gt;
&lt;span class="c"&gt;#   - Logs in CloudWatch / Datadog&lt;/span&gt;
&lt;span class="c"&gt;#   - Metrics in CloudWatch / Grafana&lt;/span&gt;
&lt;span class="c"&gt;#   - Disk grow: modify-volume + growpart + resize2fs (zero downtime)&lt;/span&gt;
&lt;span class="c"&gt;#   - Bigger instance: ASG rolling update with new instance type&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Wrap-up — Five things you'll use daily with cloud Linux: (1) fetching instance data via IMDSv2, (2) EBS volume attach + format + mount + fstab nofail, (3) bootstrapping via cloud-init user-data, (4) keyless SSH access via SSM Session Manager, (5) the golden AMI + immutable infra pattern. You'll repeat these so often they become muscle memory.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; The interview question that lands you a senior-level (USD six-figure) offer — "Describe what happens from &lt;code&gt;aws ec2 run-instances&lt;/code&gt; until an application is serving traffic." Answer: an EBS root volume is created from the AMI snapshot, an ENI is attached, the instance boots (UEFI → GRUB → kernel → initramfs → systemd) → cloud-init pulls user-data from IMDS → packages install, config files are written, services start → the application binds to its port → the health check passes → the ALB target group marks it healthy → traffic routes. If you can explain that whole chain in 60 seconds, the job is yours.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Further Learning
&lt;/h2&gt;

&lt;p&gt;You've built the foundation. Now you need to keep the muscle warm. This section is a curated map of where to go next — channels, platforms, books, and certifications that actually move the needle in the global DevOps market. Spend 30 minutes a day here for the next three months and you will be unrecognizable.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Study advice #1 — Pick ONE channel and finish it. Don't channel-hop. Bookmark 50 videos and watch zero. Pick Abhishek Veeramalla's Linux + DevOps playlist OR NetworkChuck's Linux for hackers, and finish it end-to-end before you touch anything else.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  YouTube channels worth your subscription
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Concept-first creators (great for building intuition):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Abhishek Veeramalla&lt;/strong&gt; — a gold standard for DevOps aspirants. His "DevOps Zero to Hero" playlist is one of the most recommended free resources in DevOps communities. Real interview questions, real scenarios.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Telusko (Navin Reddy)&lt;/strong&gt; — clean Linux fundamentals, scripting basics, Docker primers. Great when you want a relaxed explanation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cyber Platter&lt;/strong&gt; — strong on shell scripting, system administration, and Red Hat workflows. Useful for RHCSA prep.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Production-polished creators (depth + polish):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;NetworkChuck&lt;/strong&gt; — high-energy, hands-on. His "Linux for Hackers" series is the most fun way to fall in love with the terminal.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Learn Linux TV (Jay LaCroix)&lt;/strong&gt; — calm, thorough, no-fluff. The Ubuntu Server and Bash scripting series are excellent.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TechWorld with Nana&lt;/strong&gt; — best DevOps overview channel on YouTube. Her Docker, Kubernetes, and CI/CD crash courses are essentially free certification prep.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DistroTube&lt;/strong&gt; — pure Linux culture, window managers, dotfiles, tooling. Watch when you want to fall deeper down the rabbit hole.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Free practice platforms (this is where skill actually grows)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;OverTheWire — Bandit&lt;/strong&gt; (&lt;code&gt;overthewire.org/wargames/bandit&lt;/code&gt;) — 34 levels of SSH-driven Linux puzzles. If you can clear Bandit 0–20, you are operationally literate. This is non-negotiable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SadServers&lt;/strong&gt; (&lt;code&gt;sadservers.com&lt;/code&gt;) — "broken Linux server" scenarios. You SSH in, diagnose, and fix. This is the closest thing to a real on-call shift you can practice for free.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Linux Journey&lt;/strong&gt; (&lt;code&gt;linuxjourney.com&lt;/code&gt;) — beautifully structured beginner-to-intermediate text path. Perfect for revision.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;KillerCoda&lt;/strong&gt; (&lt;code&gt;killercoda.com&lt;/code&gt;) — browser-based Linux, Docker, Kubernetes labs. Free tier is generous.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HackTheBox&lt;/strong&gt; — privilege escalation and CTF-style Linux. Sharpens your understanding of permissions, processes, and networking.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TryHackMe&lt;/strong&gt; — gentler than HTB. Their "Linux Fundamentals" and "Linux PrivEsc" rooms are interview-grade prep.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LeetCode Shell&lt;/strong&gt; — small but mighty. Bash one-liners that show up in screening rounds (word frequency, transpose file, valid phone numbers).&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; Study advice #2 — Tutorial hell is real. For every 1 hour of video you watch, spend 2 hours in a terminal breaking things. Recruiters do not care that you watched 200 hours of content. They care that you can fix a full disk in 90 seconds on a shared screen.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Documentation — the most underrated tier
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;man&lt;/code&gt;&lt;/strong&gt; — the original. &lt;code&gt;man bash&lt;/code&gt;, &lt;code&gt;man 5 crontab&lt;/code&gt;, &lt;code&gt;man 7 signal&lt;/code&gt;. Learn to navigate with &lt;code&gt;/&lt;/code&gt; and &lt;code&gt;n&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;tldr&lt;/code&gt;&lt;/strong&gt; — community-maintained "just the examples" pages. Install with &lt;code&gt;npm i -g tldr&lt;/code&gt; or &lt;code&gt;apt install tldr&lt;/code&gt;. &lt;code&gt;tldr tar&lt;/code&gt; beats reading the man page when you just need the syntax.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;info&lt;/code&gt;&lt;/strong&gt; — GNU's hyperlinked docs. Heavier than man, but the coreutils info pages are gold.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DigitalOcean tutorials&lt;/strong&gt; (&lt;code&gt;digitalocean.com/community/tutorials&lt;/code&gt;) — the cleanest English Linux/server tutorials on the internet. Tagged by Ubuntu version. Bookmark this.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ArchWiki&lt;/strong&gt; (&lt;code&gt;wiki.archlinux.org&lt;/code&gt;) — even if you never touch Arch, the ArchWiki is the most accurate, distro-agnostic Linux reference that exists.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;tldp.org&lt;/strong&gt; — The Linux Documentation Project. Older but the "Advanced Bash-Scripting Guide" is a classic.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Books that earn shelf space
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;"How Linux Works" by Brian Ward (No Starch Press)&lt;/strong&gt; — the single best book to understand &lt;em&gt;why&lt;/em&gt; Linux behaves the way it does. Bootloaders, init, devices, networking. Read it twice.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"The Linux Command Line" by William Shotts&lt;/strong&gt; — legally free PDF at &lt;code&gt;linuxcommand.org/tlcl.php&lt;/code&gt;. The reference for shell mastery.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"Linux Bible" by Christopher Negus&lt;/strong&gt; — fat, slightly dry, but covers RHEL/Fedora workflows thoroughly. Perfect companion for RHCSA.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"Site Reliability Engineering" (Google SRE Book)&lt;/strong&gt; — free at &lt;code&gt;sre.google/books&lt;/code&gt;. Not Linux-specific but it teaches you how to &lt;em&gt;think&lt;/em&gt; like the engineers earning top-of-market (USD six-figure) salaries.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bonus: "UNIX and Linux System Administration Handbook" by Nemeth et al.&lt;/strong&gt; — the "purple book." If you go senior sysadmin, this is the bible.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Certifications — what actually matters in the DevOps market
&lt;/h3&gt;

&lt;p&gt;Ranked by ROI for someone at your stage (an experienced developer pivoting to DevOps, targeting six-figure USD roles):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;RHCSA (Red Hat Certified System Administrator) — EX200.&lt;/strong&gt; The most respected Linux cert in the industry. Hands-on exam, no MCQs. Recruiters at enterprises, Red Hat partners, and most product companies recognize it instantly. It isn't cheap, but it's worth it if your goal is Linux-heavy DevOps roles.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LFCS (Linux Foundation Certified Sysadmin).&lt;/strong&gt; Vendor-neutral, performance-based, cheaper (~$300, often 50% off). Less brand recognition than RHCSA but globally respected.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CompTIA Linux+.&lt;/strong&gt; Decent foundational cert. MCQ-based. Good for resumes scanned by ATS bots, weaker signal to senior engineers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LPIC-1.&lt;/strong&gt; Vendor-neutral, two exams. Solid content but lower brand pull than RHCSA.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Honest take:&lt;/strong&gt; &lt;strong&gt;AWS Solutions Architect Associate (SAA-C03) + provable Linux skills (Bandit + a GitHub of scripts) beats any pure-Linux cert&lt;/strong&gt; for six-figure (USD) DevOps roles. Hiring managers want cloud + Linux, not Linux alone. If you can only afford one cert this year, do SAA-C03 first, then RHCSA.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Online sandboxes (practice without burning your laptop)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Killercoda&lt;/strong&gt; — instant Ubuntu/CentOS/K8s playgrounds in the browser. No signup needed for basic labs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JSLinux&lt;/strong&gt; (&lt;code&gt;bellard.org/jslinux&lt;/code&gt;) — a full Linux kernel running in your browser via JavaScript. Useful for quick "what does this command do" checks on a locked-down machine.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DigitalOcean&lt;/strong&gt; — $200 free credit for new accounts, droplets from ~$6/month after. Cleanest UX for spinning up real VMs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Linode (Akamai)&lt;/strong&gt; — similar to DO, $100 credit, slightly cheaper at the low end.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS EC2 free tier&lt;/strong&gt; — t2.micro / t3.micro free for 12 months. This should be your daily-driver sandbox because it also teaches you AWS.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GCP free tier&lt;/strong&gt; — e2-micro is free forever in select regions. Great for a persistent personal Linux server.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Study advice #3 — Build in public. Push every script, every dotfile, every broken-then-fixed config to a GitHub repo called &lt;code&gt;linux-journey-2026&lt;/code&gt;. Tweet/LinkedIn one thing you learned each week. In six months that repo + timeline is worth more than any certificate, because it's &lt;em&gt;proof of consistent practice&lt;/em&gt; — which is exactly what DevOps hiring managers screen for.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Next Steps After Linux
&lt;/h2&gt;

&lt;p&gt;Linux is the floor, not the ceiling. Every DevOps tool you'll touch — Docker, Kubernetes, Ansible, Terraform, AWS, GCP, CI/CD runners — is just Linux wearing a costume. Here's how each next step builds directly on what you already know.&lt;/p&gt;

&lt;h3&gt;
  
  
  Linux → Bash (automate everything you just learned)
&lt;/h3&gt;

&lt;p&gt;You already know &lt;code&gt;cp&lt;/code&gt;, &lt;code&gt;grep&lt;/code&gt;, &lt;code&gt;find&lt;/code&gt;, &lt;code&gt;awk&lt;/code&gt;, &lt;code&gt;sed&lt;/code&gt;. Bash scripting is just gluing those into reusable files. The leap is small but career-changing: a sysadmin who writes scripts becomes a DevOps engineer.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Goal: be able to write a 50-line script with functions, argument parsing, error handling, and logging without Googling syntax.&lt;/li&gt;
&lt;li&gt;Resource: "Bash Scripting Cheatsheet" (devhints.io/bash) + ShellCheck (&lt;code&gt;shellcheck.net&lt;/code&gt;) for every script you write.&lt;/li&gt;
&lt;li&gt;Project: write a backup script that tars a directory, uploads to S3, rotates old backups, and emails on failure. That one script demonstrates 80% of what mid-level DevOps does daily.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Linux → Docker (namespaces + cgroups + overlayfs, nothing magical)
&lt;/h3&gt;

&lt;p&gt;Docker is not a separate technology. It's three Linux kernel features in a trench coat: &lt;strong&gt;namespaces&lt;/strong&gt; (process/network/mount isolation), &lt;strong&gt;cgroups&lt;/strong&gt; (CPU/memory limits), and &lt;strong&gt;overlayfs&lt;/strong&gt; (layered filesystems). You already learned about processes, mounts, and users — Docker is just those concepts with sharper edges.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7-day Docker plan:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Day 1:&lt;/strong&gt; Install Docker. Run &lt;code&gt;docker run -it ubuntu bash&lt;/code&gt;. Explore. Notice it feels exactly like a Linux box because it &lt;em&gt;is&lt;/em&gt; one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Day 2:&lt;/strong&gt; Images vs containers. &lt;code&gt;docker pull&lt;/code&gt;, &lt;code&gt;docker ps -a&lt;/code&gt;, &lt;code&gt;docker exec&lt;/code&gt;, &lt;code&gt;docker logs&lt;/code&gt;. Build mental model.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Day 3:&lt;/strong&gt; Write your first Dockerfile. Containerize a simple Node/React app (use your existing skills).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Day 4:&lt;/strong&gt; Volumes and bind mounts. Networks (&lt;code&gt;bridge&lt;/code&gt;, &lt;code&gt;host&lt;/code&gt;, &lt;code&gt;none&lt;/code&gt;). Port publishing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Day 5:&lt;/strong&gt; docker-compose. Spin up a 3-service stack (React + Node API + Postgres).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Day 6:&lt;/strong&gt; Image optimization — multi-stage builds, alpine base images, &lt;code&gt;.dockerignore&lt;/code&gt;. Cut a 1.2GB image to 80MB.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Day 7:&lt;/strong&gt; Push to Docker Hub. Pull on a cloud VM. Run in production-like mode with restart policies and healthchecks.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Linux → Kubernetes (nodes are Linux, pods are processes)
&lt;/h3&gt;

&lt;p&gt;A Kubernetes cluster is a fleet of Linux machines (&lt;em&gt;nodes&lt;/em&gt;) running containers (&lt;em&gt;pods&lt;/em&gt;, which are groups of Linux processes) coordinated by an API server. Everything you debug in K8s ultimately resolves to &lt;code&gt;kubectl exec -it pod -- sh&lt;/code&gt; followed by the same Linux commands you already know.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Path:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install &lt;strong&gt;minikube&lt;/strong&gt; or &lt;strong&gt;kind&lt;/strong&gt; locally. One-node cluster on your laptop.&lt;/li&gt;
&lt;li&gt;Learn pods → deployments → services → ingress in that order. Don't skip ahead.&lt;/li&gt;
&lt;li&gt;Do the free &lt;strong&gt;Kubernetes Basics&lt;/strong&gt; tutorial on kubernetes.io.&lt;/li&gt;
&lt;li&gt;KillerCoda K8s scenarios — 30 minutes/day for 3 weeks.&lt;/li&gt;
&lt;li&gt;Aim for &lt;strong&gt;CKAD (Certified Kubernetes Application Developer)&lt;/strong&gt;. It's hands-on, performance-based, and can add a meaningful bump to your offer. CKA comes later when you're ops-focused.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Linux → AWS (EC2 is Linux, Lambda is Linux, Fargate is Linux)
&lt;/h3&gt;

&lt;p&gt;You will be shocked how much of "learning AWS" is actually "applying Linux on rented hardware." An EC2 instance is a Linux VM. A Lambda function runs on Amazon Linux 2. ECS Fargate runs containers on Linux. RDS is Postgres/MySQL on Linux. The cloud is just someone else's Linux box with an API in front.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Path:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;AWS Solutions Architect Associate (SAA-C03)&lt;/strong&gt; — your gateway cert. ~$150, three months of evening study. Stephane Maarek's Udemy course is the standard.&lt;/li&gt;
&lt;li&gt;Hands-on: build a 3-tier app (VPC + EC2 + RDS + S3 + CloudFront) and tear it down with Terraform.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS DevOps Engineer Professional&lt;/strong&gt; — once you have 1+ year of real AWS experience. This is the cert that anchors senior-level (USD six-figure) conversations.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  30-day Linux-to-DevOps ramp (one focused task per day)
&lt;/h3&gt;

&lt;p&gt;Ready to apply what you've learned? Work through the &lt;a href="https://opscanopy.com/learn/guides/devops-projects/" rel="noopener noreferrer"&gt;Hands-on DevOps Projects&lt;/a&gt; to build real systems with Linux, Docker, and CI/CD.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install Ubuntu 24.04 in a VM. Customize &lt;code&gt;.bashrc&lt;/code&gt;, install zsh + oh-my-zsh.&lt;/li&gt;
&lt;li&gt;Complete OverTheWire Bandit levels 0–10.&lt;/li&gt;
&lt;li&gt;Complete Bandit levels 11–20.&lt;/li&gt;
&lt;li&gt;Write a shell script that audits your home directory: file count, largest files, oldest files.&lt;/li&gt;
&lt;li&gt;Master &lt;code&gt;grep&lt;/code&gt;, &lt;code&gt;awk&lt;/code&gt;, &lt;code&gt;sed&lt;/code&gt; with the GNU "Mastering Text Processing" cheatsheet.&lt;/li&gt;
&lt;li&gt;Set up SSH key auth between your laptop and a free-tier EC2 instance.&lt;/li&gt;
&lt;li&gt;Write a systemd service unit for a Node.js app. Enable, start, check status, view logs with journalctl.&lt;/li&gt;
&lt;li&gt;Configure UFW or iptables on your VM. Allow only SSH + HTTP/HTTPS.&lt;/li&gt;
&lt;li&gt;Install nginx, serve a static site, configure a reverse proxy to a Node app on :3000.&lt;/li&gt;
&lt;li&gt;Add Let's Encrypt (certbot) for HTTPS on a free domain (DuckDNS or Freenom).&lt;/li&gt;
&lt;li&gt;Write a cron job that backs up &lt;code&gt;/etc&lt;/code&gt; nightly to a tarball.&lt;/li&gt;
&lt;li&gt;Learn &lt;code&gt;rsync&lt;/code&gt;. Sync the backups to an S3 bucket using the AWS CLI.&lt;/li&gt;
&lt;li&gt;Read /proc/cpuinfo, /proc/meminfo. Write a script that summarizes system health.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;strace&lt;/code&gt; on a misbehaving process. Identify which syscall is failing.&lt;/li&gt;
&lt;li&gt;Install Docker. Run &lt;code&gt;hello-world&lt;/code&gt;, then Ubuntu, then nginx.&lt;/li&gt;
&lt;li&gt;Containerize your favorite React side project. Write the Dockerfile from scratch.&lt;/li&gt;
&lt;li&gt;Multi-stage build: cut your image size by 80%.&lt;/li&gt;
&lt;li&gt;Write a docker-compose.yml for React + Express + Postgres + Redis.&lt;/li&gt;
&lt;li&gt;Push your image to Docker Hub. Pull and run it on the EC2 instance.&lt;/li&gt;
&lt;li&gt;Install minikube locally. Deploy your app as a Kubernetes Deployment + Service.&lt;/li&gt;
&lt;li&gt;Add a Kubernetes ConfigMap and Secret. Wire them to your pod.&lt;/li&gt;
&lt;li&gt;Add Horizontal Pod Autoscaler. Generate load with &lt;code&gt;hey&lt;/code&gt; or &lt;code&gt;ab&lt;/code&gt; and watch it scale.&lt;/li&gt;
&lt;li&gt;Write a Bash script that deploys your app: build image → push → kubectl apply → check rollout.&lt;/li&gt;
&lt;li&gt;Set up GitHub Actions: on push to main, build Docker image and push to Docker Hub.&lt;/li&gt;
&lt;li&gt;Extend GitHub Actions: SSH to EC2 and pull the new image (a tiny CD pipeline).&lt;/li&gt;
&lt;li&gt;Sign up for AWS free tier. Launch an EC2 via the console, then via AWS CLI.&lt;/li&gt;
&lt;li&gt;Create an S3 bucket. Upload, download, set lifecycle policy to delete after 30 days.&lt;/li&gt;
&lt;li&gt;Write a Terraform file that creates a VPC + 1 EC2 + 1 S3 bucket. Apply, destroy, repeat.&lt;/li&gt;
&lt;li&gt;Install Prometheus + Grafana on minikube. Scrape node metrics. Build one dashboard.&lt;/li&gt;
&lt;li&gt;Final boss: deploy your React + Express + Postgres app to EC2, behind nginx with HTTPS, with a GitHub Actions pipeline that auto-deploys on push, and a cron-based DB backup to S3. Take a screenshot. Post it on LinkedIn with the GitHub repo. You are now a junior DevOps engineer.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  30-60-90 day vision (and the 6-month payoff)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Day 30 — Linux comfort.&lt;/strong&gt; You can SSH anywhere, debug a stuck process, write a 100-line bash script, and edit any system config without panic. You speak fluent terminal.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Day 60 — Docker + AWS basics.&lt;/strong&gt; You've containerized 3+ apps, you understand VPC/EC2/S3/IAM, and you've deployed something real to production with HTTPS. You can answer "explain Docker to a junior" in an interview.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Day 90 — K8s + CI/CD interview-ready.&lt;/strong&gt; You've passed (or can pass) CKAD-style scenarios, you have a working GitHub Actions pipeline in a public repo, and you've cleared 3-5 mock interviews. &lt;strong&gt;Target: junior/mid DevOps roles at strong product companies.&lt;/strong&gt; With your background, recruiters will fast-track you because you bring product sense most freshers lack.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Month 6 — Mid-level inflection.&lt;/strong&gt; One real production system on your resume, SAA-C03 passed, CKAD passed, contributing to an open-source DevOps tool. &lt;strong&gt;Target: mid-level DevOps / SRE / Platform Engineer roles.&lt;/strong&gt; The frontend-to-DevOps story becomes your superpower — you understand what developers actually need from a platform, which most pure-ops engineers don't.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Prefer a day-by-day path? This is covered in &lt;a href="https://opscanopy.com/mission-90/" rel="noopener noreferrer"&gt;&lt;strong&gt;Mission 90 Days 1–20&lt;/strong&gt;&lt;/a&gt; — a free 90-day guided DevOps program with browser terminal missions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Real world:&lt;/strong&gt; The pep talk you came here for. You are not late. The global DevOps market in 2026 is desperate for engineers who can talk to developers &lt;em&gt;and&lt;/em&gt; ops — because most candidates can only do one. Your years of shipping real features is not a liability; it is a wedge. Recruiters will pay a premium for someone who has actually pushed code to production and now understands the pipeline that delivers it.&lt;/p&gt;

&lt;p&gt;Six months from now, a hiring manager is going to look at your GitHub, see 180 days of consistent commits, see one solid end-to-end project, see SAA-C03 and maybe CKAD on the resume, and make you a strong offer without blinking. The only thing standing between you and that offer is showing up to the terminal every day between now and then.&lt;/p&gt;

&lt;p&gt;Close this tab. Open one. Type &lt;code&gt;ssh&lt;/code&gt;. Begin.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>linux</category>
      <category>bash</category>
      <category>systemd</category>
      <category>ssh</category>
    </item>
    <item>
      <title>Kubernetes for DevOps: Pods, Deployments, Services &amp; More</title>
      <dc:creator>opscanopy</dc:creator>
      <pubDate>Wed, 19 Aug 2026 15:31:42 +0000</pubDate>
      <link>https://dev.to/opscanopy/kubernetes-for-devops-pods-deployments-services-more-22af</link>
      <guid>https://dev.to/opscanopy/kubernetes-for-devops-pods-deployments-services-more-22af</guid>
      <description>&lt;p&gt;Kubernetes (K8s) has become the standard runtime fabric for containerised workloads. Whether you are deploying a single microservice or coordinating hundreds of them across multiple availability zones, K8s gives you a consistent, declarative API to describe what should run, where it should run, and how it should behave. This guide walks through the core concepts a DevOps engineer needs day-to-day — cluster architecture, workload primitives, networking, configuration, observability, and troubleshooting — with working YAML and &lt;code&gt;kubectl&lt;/code&gt; commands throughout. To see where Kubernetes fits in the broader learning journey, check the &lt;a href="https://opscanopy.com/learn/roadmaps/kubernetes/" rel="noopener noreferrer"&gt;Kubernetes roadmap&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Kubernetes?
&lt;/h2&gt;

&lt;p&gt;Running containers in production with plain Docker quickly exposes a set of operational problems: who restarts a crashed container, how do you roll out a new image version without downtime, how do you balance traffic across multiple replicas, and how do you ensure a container on a node with 64 GB RAM does not starve a neighbouring container of memory?&lt;/p&gt;

&lt;p&gt;Kubernetes solves these problems through the &lt;strong&gt;desired-state model&lt;/strong&gt;: you tell the cluster what the end state should look like (a YAML manifest), and controllers continuously reconcile actual state toward that goal. This gives you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Self-healing&lt;/strong&gt; — crashed containers are restarted; failed nodes trigger Pod rescheduling.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Horizontal scaling&lt;/strong&gt; — add replicas with one command or automatically via HPA.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rolling updates and rollbacks&lt;/strong&gt; — new versions roll out gradually; one command reverts them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Service discovery and load balancing&lt;/strong&gt; — Pods get stable DNS names without static IPs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Declarative configuration&lt;/strong&gt; — your entire infrastructure lives in version-controlled YAML.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Kubernetes does not replace a CI/CD pipeline. It is the runtime target; tools like Argo CD, Flux, or Jenkins deploy to it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Cluster Architecture
&lt;/h2&gt;

&lt;p&gt;Understanding the architecture prevents a lot of confusion when things go wrong.&lt;/p&gt;

&lt;h3&gt;
  
  
  Control Plane
&lt;/h3&gt;

&lt;p&gt;The control plane is the brain of the cluster. In managed services (EKS, GKE, AKS) it is operated for you; in self-managed setups you are responsible for its reliability.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;kube-apiserver&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Single entry point for all cluster operations. Validates and persists objects to etcd. Every &lt;code&gt;kubectl&lt;/code&gt; command talks to this.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;etcd&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Distributed key-value store. Holds all cluster state. Losing etcd without a backup loses your cluster.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;kube-scheduler&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Watches for new Pods with no assigned node and picks the best node based on resource requests, taints, affinity rules, and topology spread.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;kube-controller-manager&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Runs core controllers: ReplicaSet, Deployment, Node, Endpoints, ServiceAccount, and others. Each controller reconciles its resource type.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;cloud-controller-manager&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;(Cloud clusters only) Interfaces with cloud provider APIs — creates load balancers, routes, storage volumes.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Worker Nodes
&lt;/h3&gt;

&lt;p&gt;Every node that runs workloads must have three components:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;kubelet&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Agent on each node. Receives PodSpec from the API server and instructs the container runtime to start/stop containers. Reports node and Pod status.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;kube-proxy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Maintains iptables/ipvs rules that implement Service networking — forwarding traffic to the correct Pod IPs.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Container runtime&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Actually runs containers. Kubernetes supports any CRI-compatible runtime: containerd (most common), CRI-O, or Docker (via cri-dockerd shim).&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; In production, the control plane nodes are typically tainted so user workloads do not schedule on them. Managed services do this automatically.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  How a Pod Gets Scheduled
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;You run &lt;code&gt;kubectl apply -f pod.yaml&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;kube-apiserver&lt;/code&gt; validates the manifest and stores it in etcd.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;kube-scheduler&lt;/code&gt; notices a Pod with no &lt;code&gt;nodeName&lt;/code&gt; and selects a node.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;kubelet&lt;/code&gt; on that node watches the API server, sees the Pod assigned to it, pulls the image, and starts the container.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;kube-proxy&lt;/code&gt; updates network rules so the Pod is reachable via its Service.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  &lt;/p&gt;
Kubernetes cluster architecture: control plane (API server, etcd, scheduler, controller-manager) communicates with worker nodes (kubelet, kube-proxy, runtime) that host Pods.





&lt;h2&gt;
  
  
  Pods
&lt;/h2&gt;

&lt;p&gt;A Pod is the smallest schedulable unit in Kubernetes. It wraps one or more containers (built from &lt;a href="https://opscanopy.com/learn/guides/docker-for-devops/" rel="noopener noreferrer"&gt;Docker for DevOps&lt;/a&gt; images) that share:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Network namespace&lt;/strong&gt; — all containers in a Pod share the same IP address and port space.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Storage&lt;/strong&gt; — volumes are declared at the Pod level and mounted into individual containers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lifecycle&lt;/strong&gt; — all containers start and stop together with the Pod.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Minimal Pod Manifest
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Pod&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;web-pod&lt;/span&gt;
  &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;web&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx&lt;/span&gt;
      &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx:1.27&lt;/span&gt;
      &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;containerPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; pod.yaml
kubectl get pods
kubectl describe pod web-pod
kubectl delete pod web-pod
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Pod Lifecycle
&lt;/h3&gt;

&lt;p&gt;A Pod moves through these phases:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Phase&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Pending&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pod accepted by the cluster; containers not yet started (pulling images, waiting for node).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Running&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;At least one container is running or starting.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Succeeded&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;All containers exited with code 0 (typical for Jobs).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Failed&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;At least one container exited with non-zero code and will not restart.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Unknown&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Node communication lost; state cannot be determined.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Multi-Container Patterns
&lt;/h3&gt;

&lt;p&gt;Pods can host multiple containers. The three canonical patterns are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Sidecar&lt;/strong&gt; — a helper container that augments the main one (e.g., a log-shipper or a service mesh proxy like Envoy/Istio).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Init container&lt;/strong&gt; — runs to completion &lt;em&gt;before&lt;/em&gt; the main containers start (e.g., database migration, config rendering from a vault).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ambassador&lt;/strong&gt; — a proxy container that abstracts external services (e.g., a Redis proxy in front of a remote cluster).
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Pod&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app-with-sidecar&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;initContainers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;init-migrate&lt;/span&gt;
      &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;migrate-tool:v1&lt;/span&gt;
      &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./migrate"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--up"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app&lt;/span&gt;
      &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myapp:v2&lt;/span&gt;
      &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;containerPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8080&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;log-shipper&lt;/span&gt;
      &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;fluent/fluent-bit:3.0&lt;/span&gt;
      &lt;span class="na"&gt;volumeMounts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app-logs&lt;/span&gt;
          &lt;span class="na"&gt;mountPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/var/log/app&lt;/span&gt;
  &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app-logs&lt;/span&gt;
      &lt;span class="na"&gt;emptyDir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Why You Rarely Create Bare Pods
&lt;/h3&gt;

&lt;p&gt;Bare Pods are not rescheduled if the node they are on fails. You should almost always use a higher-level workload controller (Deployment, StatefulSet, DaemonSet) that manages Pods on your behalf.&lt;/p&gt;

&lt;h2&gt;
  
  
  ReplicaSets and Deployments
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ReplicaSet
&lt;/h3&gt;

&lt;p&gt;A ReplicaSet ensures a specified number of identical Pod replicas are running at any time. It uses a label selector to identify the Pods it owns. If a Pod dies, the ReplicaSet creates a replacement.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; You almost never create ReplicaSets directly. Deployments create and manage ReplicaSets for you, adding rollout history.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Deployments
&lt;/h3&gt;

&lt;p&gt;A Deployment wraps a ReplicaSet and adds rollout logic: you update the Pod template and the Deployment gradually replaces old Pods with new ones according to the configured strategy.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;br&gt;
&lt;/p&gt;
A Deployment owns a ReplicaSet, which owns and maintains the desired number of identical Pods via a label selector.





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apps/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deployment&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;web-deployment&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;production&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;replicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;matchLabels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;web&lt;/span&gt;
  &lt;span class="na"&gt;strategy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;RollingUpdate&lt;/span&gt;
    &lt;span class="na"&gt;rollingUpdate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;maxUnavailable&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
      &lt;span class="na"&gt;maxSurge&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;web&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;web&lt;/span&gt;
          &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myapp:v2&lt;/span&gt;
          &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;containerPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8080&lt;/span&gt;
          &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;requests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;250m"&lt;/span&gt;
              &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;256Mi"&lt;/span&gt;
            &lt;span class="na"&gt;limits&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;500m"&lt;/span&gt;
              &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;512Mi"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key fields:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;selector.matchLabels&lt;/code&gt; — the Deployment manages any Pod with these labels. Must match &lt;code&gt;template.metadata.labels&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;strategy.rollingUpdate.maxUnavailable&lt;/code&gt; — how many Pods can be unavailable during a rollout (absolute or percentage).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;strategy.rollingUpdate.maxSurge&lt;/code&gt; — how many extra Pods can be created above the desired count during a rollout.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Apply or update&lt;/span&gt;
kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; deployment.yaml

&lt;span class="c"&gt;# Watch rollout progress&lt;/span&gt;
kubectl rollout status deployment/web-deployment

&lt;span class="c"&gt;# Scale manually&lt;/span&gt;
kubectl scale deployment web-deployment &lt;span class="nt"&gt;--replicas&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5

&lt;span class="c"&gt;# View rollout history&lt;/span&gt;
kubectl rollout &lt;span class="nb"&gt;history &lt;/span&gt;deployment/web-deployment

&lt;span class="c"&gt;# Rollback one version&lt;/span&gt;
kubectl rollout undo deployment/web-deployment
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Always set &lt;code&gt;resources.requests&lt;/code&gt; and &lt;code&gt;resources.limits&lt;/code&gt;. Without them, the scheduler cannot make good placement decisions and your Pods get the lowest QoS class.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Services
&lt;/h2&gt;

&lt;p&gt;A Service gives a stable DNS name and IP to a dynamic set of Pods selected by a label selector. Even as Pods are created and destroyed, the Service's IP (&lt;code&gt;ClusterIP&lt;/code&gt;) and DNS name stay constant.&lt;/p&gt;

&lt;h3&gt;
  
  
  Service Types
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Accessibility&lt;/th&gt;
&lt;th&gt;Typical Use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;ClusterIP&lt;/code&gt; (default)&lt;/td&gt;
&lt;td&gt;Inside the cluster only&lt;/td&gt;
&lt;td&gt;Internal microservice communication&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;NodePort&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Cluster nodes' IPs on a static port (30000–32767)&lt;/td&gt;
&lt;td&gt;Dev/test external access without a cloud LB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;LoadBalancer&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;External IP via cloud provider's load balancer&lt;/td&gt;
&lt;td&gt;Production external traffic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ExternalName&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;DNS alias for an external service&lt;/td&gt;
&lt;td&gt;Pointing in-cluster workloads at external endpoints&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  &lt;/p&gt;
Service types and Ingress: Ingress routes HTTP traffic by host/path to Services, while LoadBalancer and NodePort expose Services externally; ClusterIP is internal-only.





&lt;h3&gt;
  
  
  ClusterIP Example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Service&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;web-service&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;production&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;web&lt;/span&gt;
  &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TCP&lt;/span&gt;
      &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
      &lt;span class="na"&gt;targetPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8080&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ClusterIP&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pods in the same namespace can reach this service at &lt;code&gt;web-service:80&lt;/code&gt;; pods in other namespaces use &lt;code&gt;web-service.production.svc.cluster.local:80&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  LoadBalancer Example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Service&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;web-lb&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;production&lt;/span&gt;
  &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;service.beta.kubernetes.io/aws-load-balancer-type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;nlb"&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;web&lt;/span&gt;
  &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TCP&lt;/span&gt;
      &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;443&lt;/span&gt;
      &lt;span class="na"&gt;targetPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8080&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;LoadBalancer&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  How kube-proxy Implements Services
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;kube-proxy&lt;/code&gt; watches the API server for Service and Endpoints objects. When a Service is created, it programs iptables rules (or IPVS rules in IPVS mode) on every node so that traffic to the ClusterIP is DNAT'd to one of the healthy Pod IPs in the Endpoints list.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get svc &lt;span class="nt"&gt;-n&lt;/span&gt; production
kubectl describe svc web-service &lt;span class="nt"&gt;-n&lt;/span&gt; production
kubectl get endpoints web-service &lt;span class="nt"&gt;-n&lt;/span&gt; production
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; A Service with no matching Pods will have an empty Endpoints list and traffic will be silently dropped. Always check &lt;code&gt;kubectl get endpoints&lt;/code&gt; when debugging connectivity.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Ingress
&lt;/h2&gt;

&lt;p&gt;A Service of type &lt;code&gt;LoadBalancer&lt;/code&gt; creates a separate cloud load balancer per service — expensive at scale. An &lt;strong&gt;Ingress&lt;/strong&gt; is a cluster-wide HTTP(S) router that multiplexes many Services behind a single load balancer using hostname and path rules.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ingress Controller
&lt;/h3&gt;

&lt;p&gt;An Ingress object is just a configuration resource. You also need an &lt;strong&gt;Ingress controller&lt;/strong&gt; — a Pod that reads Ingress objects and programs a reverse proxy. Common choices:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;NGINX Ingress Controller&lt;/strong&gt; (most widely deployed)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS Load Balancer Controller&lt;/strong&gt; (for ALB on EKS — see &lt;a href="https://opscanopy.com/learn/guides/aws-for-devops-engineers/" rel="noopener noreferrer"&gt;AWS for DevOps Engineers&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Traefik&lt;/strong&gt; (dynamic config, good for smaller clusters)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Istio Gateway&lt;/strong&gt; (service mesh, advanced traffic management)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Ingress Manifest with TLS
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;networking.k8s.io/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Ingress&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;web-ingress&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;production&lt;/span&gt;
  &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;nginx.ingress.kubernetes.io/rewrite-target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt;
    &lt;span class="na"&gt;nginx.ingress.kubernetes.io/ssl-redirect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;true"&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;ingressClassName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx&lt;/span&gt;
  &lt;span class="na"&gt;tls&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;app.example.com&lt;/span&gt;
      &lt;span class="na"&gt;secretName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;tls-app-example-com&lt;/span&gt;
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app.example.com&lt;/span&gt;
      &lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/api&lt;/span&gt;
            &lt;span class="na"&gt;pathType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Prefix&lt;/span&gt;
            &lt;span class="na"&gt;backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;api-service&lt;/span&gt;
                &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="na"&gt;number&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt;
            &lt;span class="na"&gt;pathType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Prefix&lt;/span&gt;
            &lt;span class="na"&gt;backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;web-service&lt;/span&gt;
                &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="na"&gt;number&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;tls.secretName&lt;/code&gt; field references a Kubernetes Secret of type &lt;code&gt;kubernetes.io/tls&lt;/code&gt; that holds the certificate and key. Tools like &lt;strong&gt;cert-manager&lt;/strong&gt; automate TLS certificate provisioning and renewal via Let's Encrypt.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get ingress &lt;span class="nt"&gt;-n&lt;/span&gt; production
kubectl describe ingress web-ingress &lt;span class="nt"&gt;-n&lt;/span&gt; production
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Set &lt;code&gt;ingressClassName&lt;/code&gt; explicitly. Older clusters used annotations (&lt;code&gt;kubernetes.io/ingress.class&lt;/code&gt;), but the &lt;code&gt;ingressClassName&lt;/code&gt; field is the current standard.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  ConfigMaps
&lt;/h2&gt;

&lt;p&gt;A ConfigMap stores non-sensitive configuration as key-value pairs or files. Pods consume ConfigMaps as environment variables or as mounted files.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating a ConfigMap
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ConfigMap&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app-config&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;production&lt;/span&gt;
&lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;LOG_LEVEL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;info"&lt;/span&gt;
  &lt;span class="na"&gt;MAX_CONNECTIONS&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;100"&lt;/span&gt;
  &lt;span class="na"&gt;config.yaml&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;server:&lt;/span&gt;
      &lt;span class="s"&gt;host: 0.0.0.0&lt;/span&gt;
      &lt;span class="s"&gt;port: 8080&lt;/span&gt;
    &lt;span class="s"&gt;database:&lt;/span&gt;
      &lt;span class="s"&gt;pool_size: 10&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; configmap.yaml
kubectl get configmap app-config &lt;span class="nt"&gt;-n&lt;/span&gt; production &lt;span class="nt"&gt;-o&lt;/span&gt; yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Consuming a ConfigMap as Environment Variables
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app&lt;/span&gt;
      &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myapp:v2&lt;/span&gt;
      &lt;span class="na"&gt;envFrom&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;configMapRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app-config&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or inject individual keys:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;      &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;LOG_LEVEL&lt;/span&gt;
          &lt;span class="na"&gt;valueFrom&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;configMapKeyRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app-config&lt;/span&gt;
              &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;LOG_LEVEL&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Consuming a ConfigMap as a Mounted File
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app&lt;/span&gt;
      &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myapp:v2&lt;/span&gt;
      &lt;span class="na"&gt;volumeMounts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;config-volume&lt;/span&gt;
          &lt;span class="na"&gt;mountPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/etc/app&lt;/span&gt;
  &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;config-volume&lt;/span&gt;
      &lt;span class="na"&gt;configMap&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app-config&lt;/span&gt;
        &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;config.yaml&lt;/span&gt;
            &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;config.yaml&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The file &lt;code&gt;/etc/app/config.yaml&lt;/code&gt; inside the container will contain the YAML defined in the ConfigMap.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; When you update a ConfigMap that is mounted as a volume, Kubernetes eventually (within ~60 seconds) updates the file in the container. Environment variables injected via &lt;code&gt;envFrom&lt;/code&gt; are &lt;strong&gt;not&lt;/strong&gt; updated — the Pod must be restarted.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Secrets
&lt;/h2&gt;

&lt;p&gt;Secrets store sensitive data — passwords, API tokens, TLS certificates, SSH keys. They are similar to ConfigMaps but:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Values are &lt;strong&gt;base64-encoded&lt;/strong&gt; (not encrypted by default, but can be with encryption at rest).&lt;/li&gt;
&lt;li&gt;Access is controlled by &lt;strong&gt;RBAC&lt;/strong&gt; — separate from ConfigMap access.&lt;/li&gt;
&lt;li&gt;They integrate with external secret stores (HashiCorp Vault, AWS Secrets Manager) via CSI drivers or operators.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Secret Types
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;Opaque&lt;/code&gt; (default)&lt;/td&gt;
&lt;td&gt;Arbitrary key-value data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;kubernetes.io/tls&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;TLS certificate and key&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;kubernetes.io/dockerconfigjson&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Docker registry credentials&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;kubernetes.io/service-account-token&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;SA tokens (auto-managed)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;kubernetes.io/ssh-auth&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;SSH private keys&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;kubernetes.io/basic-auth&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Username/password pairs&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;
ConfigMaps and Secrets inject configuration into Pods as environment variables (envFrom / secretKeyRef) or as mounted files in volumes.





&lt;h3&gt;
  
  
  Creating an Opaque Secret
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Secret&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;db-credentials&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;production&lt;/span&gt;
&lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Opaque&lt;/span&gt;
&lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;YWRtaW4=&lt;/span&gt;        &lt;span class="c1"&gt;# base64("admin")&lt;/span&gt;
  &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;c3VwZXJzZWNyZXQ=&lt;/span&gt; &lt;span class="c1"&gt;# base64("supersecret")&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Encode a value&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"supersecret"&lt;/span&gt; | &lt;span class="nb"&gt;base64&lt;/span&gt;

&lt;span class="c"&gt;# Create from literal (easier)&lt;/span&gt;
kubectl create secret generic db-credentials &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--from-literal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;admin &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--from-literal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;supersecret &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-n&lt;/span&gt; production

&lt;span class="c"&gt;# View (values are base64)&lt;/span&gt;
kubectl get secret db-credentials &lt;span class="nt"&gt;-n&lt;/span&gt; production &lt;span class="nt"&gt;-o&lt;/span&gt; yaml

&lt;span class="c"&gt;# Decode a value&lt;/span&gt;
kubectl get secret db-credentials &lt;span class="nt"&gt;-n&lt;/span&gt; production &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;jsonpath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'{.data.password}'&lt;/span&gt; | &lt;span class="nb"&gt;base64&lt;/span&gt; &lt;span class="nt"&gt;--decode&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Consuming a Secret in a Pod
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app&lt;/span&gt;
      &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myapp:v2&lt;/span&gt;
      &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DB_USERNAME&lt;/span&gt;
          &lt;span class="na"&gt;valueFrom&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;secretKeyRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;db-credentials&lt;/span&gt;
              &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;username&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DB_PASSWORD&lt;/span&gt;
          &lt;span class="na"&gt;valueFrom&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;secretKeyRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;db-credentials&lt;/span&gt;
              &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;password&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Encryption at Rest and RBAC
&lt;/h3&gt;

&lt;p&gt;By default, Secrets in etcd are stored base64-encoded but not encrypted. Enable encryption at rest via the API server's &lt;code&gt;--encryption-provider-config&lt;/code&gt; flag using an &lt;code&gt;aescbc&lt;/code&gt; or &lt;code&gt;kms&lt;/code&gt; provider.&lt;/p&gt;

&lt;p&gt;Use RBAC to restrict which service accounts can &lt;code&gt;get&lt;/code&gt;, &lt;code&gt;list&lt;/code&gt;, or &lt;code&gt;watch&lt;/code&gt; Secrets:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rbac.authorization.k8s.io/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Role&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;secret-reader&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;production&lt;/span&gt;
&lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;apiGroups&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;secrets"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;resourceNames&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;db-credentials"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;verbs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;get"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; Never commit Secret manifests with real values to version control. Use sealed secrets (Bitnami Sealed Secrets), External Secrets Operator, or a Vault CSI driver to inject secrets at runtime.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Namespaces
&lt;/h2&gt;

&lt;p&gt;Namespaces provide a mechanism for isolating groups of resources within a single cluster. They are soft multi-tenancy: they scope names, RBAC, and resource quotas, but do not provide strong network isolation by themselves (for that, use NetworkPolicies).&lt;/p&gt;

&lt;h3&gt;
  
  
  Built-in Namespaces
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Namespace&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;default&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Default for resources with no namespace specified&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;kube-system&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Kubernetes control plane components (CoreDNS, kube-proxy, etc.)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;kube-public&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Publicly readable resources (ConfigMap with cluster info)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;kube-node-lease&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Node heartbeat lease objects&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Common Namespace Operations
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# List all namespaces&lt;/span&gt;
kubectl get namespaces

&lt;span class="c"&gt;# Create a namespace&lt;/span&gt;
kubectl create namespace staging

&lt;span class="c"&gt;# Run commands in a specific namespace&lt;/span&gt;
kubectl get pods &lt;span class="nt"&gt;-n&lt;/span&gt; staging

&lt;span class="c"&gt;# Set a default namespace for your context&lt;/span&gt;
kubectl config set-context &lt;span class="nt"&gt;--current&lt;/span&gt; &lt;span class="nt"&gt;--namespace&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;staging

&lt;span class="c"&gt;# View resources across all namespaces&lt;/span&gt;
kubectl get pods &lt;span class="nt"&gt;--all-namespaces&lt;/span&gt;
&lt;span class="c"&gt;# or&lt;/span&gt;
kubectl get pods &lt;span class="nt"&gt;-A&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ResourceQuota and LimitRange
&lt;/h3&gt;

&lt;p&gt;Namespaces support &lt;code&gt;ResourceQuota&lt;/code&gt; (cap total resource usage per namespace) and &lt;code&gt;LimitRange&lt;/code&gt; (set default and max limits per container).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ResourceQuota&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;team-quota&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;staging&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;hard&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;requests.cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;4"&lt;/span&gt;
    &lt;span class="na"&gt;requests.memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;8Gi&lt;/span&gt;
    &lt;span class="na"&gt;limits.cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8"&lt;/span&gt;
    &lt;span class="na"&gt;limits.memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;16Gi&lt;/span&gt;
    &lt;span class="na"&gt;count/pods&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;20"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Resource Requests and Limits
&lt;/h2&gt;

&lt;p&gt;Getting resource configuration right is one of the highest-leverage tuning tasks in Kubernetes.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Concepts
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Request&lt;/strong&gt;: the amount of CPU/memory the scheduler &lt;em&gt;reserves&lt;/em&gt; on a node for this container. The node must have at least this much available for the Pod to be scheduled there.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Limit&lt;/strong&gt;: the maximum CPU/memory the container is allowed to use. Exceeding the CPU limit causes throttling; exceeding the memory limit causes the container to be OOM-killed.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;requests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;250m"&lt;/span&gt;      &lt;span class="c1"&gt;# 250 millicores = 0.25 CPU core&lt;/span&gt;
    &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;256Mi"&lt;/span&gt;
  &lt;span class="na"&gt;limits&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;500m"&lt;/span&gt;
    &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;512Mi"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;CPU is expressed in cores (&lt;code&gt;1&lt;/code&gt;, &lt;code&gt;0.5&lt;/code&gt;) or millicores (&lt;code&gt;500m&lt;/code&gt;). Memory uses binary suffixes (&lt;code&gt;Ki&lt;/code&gt;, &lt;code&gt;Mi&lt;/code&gt;, &lt;code&gt;Gi&lt;/code&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  QoS Classes
&lt;/h3&gt;

&lt;p&gt;Kubernetes assigns one of three Quality of Service classes to every Pod based on its resource configuration:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;QoS Class&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;Eviction Priority&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Guaranteed&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Every container has &lt;code&gt;requests == limits&lt;/code&gt; for both CPU and memory&lt;/td&gt;
&lt;td&gt;Last to be evicted&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Burstable&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;At least one container has a request or limit, but not Guaranteed&lt;/td&gt;
&lt;td&gt;Middle priority&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;BestEffort&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No container has any request or limit&lt;/td&gt;
&lt;td&gt;First to be evicted under pressure&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;
CPU/memory request (scheduler reservation) versus limit (hard ceiling), and the three QoS classes that determine eviction order under node pressure.





&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; For production workloads, aim for &lt;strong&gt;Guaranteed&lt;/strong&gt; or &lt;strong&gt;Burstable&lt;/strong&gt; QoS. Use &lt;code&gt;kubectl describe pod &amp;lt;name&amp;gt;&lt;/code&gt; and look for &lt;code&gt;QoS Class:&lt;/code&gt; in the output.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Setting the Right Numbers
&lt;/h3&gt;

&lt;p&gt;Right-sizing requests and limits requires profiling your actual workload under load. Size CPU/memory requests and limits with the &lt;a href="https://opscanopy.com/kubernetes-resource-calculator/" rel="noopener noreferrer"&gt;Kubernetes Resource Calculator&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Common mistakes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Setting limits much higher than requests creates "noisy neighbour" risk.&lt;/li&gt;
&lt;li&gt;Setting limits equal to very low requests may cause OOMKills under normal spikes.&lt;/li&gt;
&lt;li&gt;Not setting requests at all results in BestEffort Pods that are first to go under node pressure.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Vertical Pod Autoscaler (VPA) and Horizontal Pod Autoscaler (HPA)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;HPA&lt;/strong&gt; scales the &lt;em&gt;number&lt;/em&gt; of replicas based on CPU utilisation, memory, or custom metrics.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;VPA&lt;/strong&gt; adjusts the &lt;em&gt;requests/limits&lt;/em&gt; of existing containers (requires Pod restarts).
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;autoscaling/v2&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;HorizontalPodAutoscaler&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;web-hpa&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;production&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;scaleTargetRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apps/v1&lt;/span&gt;
    &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deployment&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;web-deployment&lt;/span&gt;
  &lt;span class="na"&gt;minReplicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
  &lt;span class="na"&gt;maxReplicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20&lt;/span&gt;
  &lt;span class="na"&gt;metrics&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Resource&lt;/span&gt;
      &lt;span class="na"&gt;resource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cpu&lt;/span&gt;
        &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Utilization&lt;/span&gt;
          &lt;span class="na"&gt;averageUtilization&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;60&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Health Probes
&lt;/h2&gt;

&lt;p&gt;Kubernetes uses three probe types to determine container health. Without probes, a container is considered healthy the moment it starts — even if the application inside is not ready yet.&lt;/p&gt;

&lt;h3&gt;
  
  
  Probe Types
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Probe&lt;/th&gt;
&lt;th&gt;Kubernetes Action on Failure&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Liveness&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Restart the container. Use for detecting deadlocks — processes that are running but stuck.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Readiness&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Remove the Pod from Service endpoints. Traffic stops flowing to it, but the container is not restarted.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Startup&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Disables liveness and readiness until it succeeds. Use for slow-starting apps to prevent premature liveness restarts.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Probe Mechanisms
&lt;/h3&gt;

&lt;p&gt;Each probe type can use one of three mechanisms:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;httpGet&lt;/code&gt; — HTTP GET request; success = 2xx/3xx response.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tcpSocket&lt;/code&gt; — TCP connection; success = port is open.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;exec&lt;/code&gt; — run a command inside the container; success = exit code 0.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Full Probe Example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app&lt;/span&gt;
      &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myapp:v2&lt;/span&gt;
      &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;containerPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8080&lt;/span&gt;
      &lt;span class="na"&gt;startupProbe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;httpGet&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/healthz&lt;/span&gt;
          &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8080&lt;/span&gt;
        &lt;span class="na"&gt;failureThreshold&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;30&lt;/span&gt;
        &lt;span class="na"&gt;periodSeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
      &lt;span class="na"&gt;readinessProbe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;httpGet&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/ready&lt;/span&gt;
          &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8080&lt;/span&gt;
        &lt;span class="na"&gt;initialDelaySeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
        &lt;span class="na"&gt;periodSeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
        &lt;span class="na"&gt;failureThreshold&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
        &lt;span class="na"&gt;successThreshold&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
      &lt;span class="na"&gt;livenessProbe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;httpGet&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/healthz&lt;/span&gt;
          &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8080&lt;/span&gt;
        &lt;span class="na"&gt;initialDelaySeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;30&lt;/span&gt;
        &lt;span class="na"&gt;periodSeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;15&lt;/span&gt;
        &lt;span class="na"&gt;failureThreshold&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
        &lt;span class="na"&gt;timeoutSeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key parameters:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parameter&lt;/th&gt;
&lt;th&gt;Default&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;initialDelaySeconds&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;Seconds after container start before first probe&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;periodSeconds&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;How often to probe&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;failureThreshold&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Failures before action is taken&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;successThreshold&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Successes needed to transition back to healthy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;timeoutSeconds&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Seconds to wait for probe response&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Always implement separate &lt;code&gt;/healthz&lt;/code&gt; (liveness) and &lt;code&gt;/ready&lt;/code&gt; (readiness) endpoints in your application. The readiness endpoint should check downstream dependencies (database reachability, cache connection); the liveness endpoint should only check the application itself.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Rollouts and Rollbacks
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Rolling Update Strategy
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;RollingUpdate&lt;/code&gt; strategy (default for Deployments) replaces old Pods with new ones incrementally. The &lt;code&gt;Recreate&lt;/code&gt; strategy terminates all old Pods before starting new ones — useful when your app cannot run two versions simultaneously.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Trigger a rollout by updating the image&lt;/span&gt;
kubectl &lt;span class="nb"&gt;set &lt;/span&gt;image deployment/web-deployment &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nv"&gt;web&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;myapp:v3 &lt;span class="nt"&gt;-n&lt;/span&gt; production

&lt;span class="c"&gt;# Or edit the manifest and apply&lt;/span&gt;
kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; deployment.yaml

&lt;span class="c"&gt;# Watch the rollout in real time&lt;/span&gt;
kubectl rollout status deployment/web-deployment &lt;span class="nt"&gt;-n&lt;/span&gt; production

&lt;span class="c"&gt;# Pause a rollout mid-way (canary-style)&lt;/span&gt;
kubectl rollout pause deployment/web-deployment &lt;span class="nt"&gt;-n&lt;/span&gt; production

&lt;span class="c"&gt;# Resume&lt;/span&gt;
kubectl rollout resume deployment/web-deployment &lt;span class="nt"&gt;-n&lt;/span&gt; production
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Rollback
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# View rollout history with change-cause annotations&lt;/span&gt;
kubectl rollout &lt;span class="nb"&gt;history &lt;/span&gt;deployment/web-deployment &lt;span class="nt"&gt;-n&lt;/span&gt; production

&lt;span class="c"&gt;# View a specific revision&lt;/span&gt;
kubectl rollout &lt;span class="nb"&gt;history &lt;/span&gt;deployment/web-deployment &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--revision&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2 &lt;span class="nt"&gt;-n&lt;/span&gt; production

&lt;span class="c"&gt;# Roll back to previous version&lt;/span&gt;
kubectl rollout undo deployment/web-deployment &lt;span class="nt"&gt;-n&lt;/span&gt; production

&lt;span class="c"&gt;# Roll back to a specific revision&lt;/span&gt;
kubectl rollout undo deployment/web-deployment &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--to-revision&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2 &lt;span class="nt"&gt;-n&lt;/span&gt; production
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Annotate your rollouts with a change-cause so history is readable: add &lt;code&gt;kubernetes.io/change-cause: "bump to v3, adds auth middleware"&lt;/code&gt; to the Deployment's &lt;code&gt;metadata.annotations&lt;/code&gt; before applying.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Deployment Revision History
&lt;/h3&gt;

&lt;p&gt;By default, Kubernetes retains 10 revisions. Control this with &lt;code&gt;spec.revisionHistoryLimit&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;revisionHistoryLimit&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Observability
&lt;/h2&gt;

&lt;p&gt;A Kubernetes cluster generates a rich stream of metrics, logs, and events. Building good visibility requires three layers:&lt;/p&gt;

&lt;h3&gt;
  
  
  Logs
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;kubectl logs&lt;/code&gt; reads from the container's stdout/stderr (captured by the container runtime and available via the API server):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Follow logs from a running Pod&lt;/span&gt;
kubectl logs &lt;span class="nt"&gt;-f&lt;/span&gt; deployment/web-deployment &lt;span class="nt"&gt;-n&lt;/span&gt; production

&lt;span class="c"&gt;# Show last 100 lines&lt;/span&gt;
kubectl logs web-pod &lt;span class="nt"&gt;--tail&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;100 &lt;span class="nt"&gt;-n&lt;/span&gt; production

&lt;span class="c"&gt;# Logs from a specific container in a multi-container Pod&lt;/span&gt;
kubectl logs web-pod &lt;span class="nt"&gt;-c&lt;/span&gt; log-shipper &lt;span class="nt"&gt;-n&lt;/span&gt; production

&lt;span class="c"&gt;# Logs from a previous (crashed) container instance&lt;/span&gt;
kubectl logs web-pod &lt;span class="nt"&gt;--previous&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; production
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For persistent log aggregation, deploy a log collector DaemonSet (Fluent Bit, Fluentd) that ships container logs to a backend (Elasticsearch, Loki, CloudWatch Logs).&lt;/p&gt;

&lt;h3&gt;
  
  
  Metrics and Prometheus
&lt;/h3&gt;

&lt;p&gt;The Kubernetes ecosystem is deeply integrated with &lt;strong&gt;Prometheus&lt;/strong&gt;. The &lt;code&gt;metrics-server&lt;/code&gt; provides basic CPU/memory metrics for HPA and &lt;code&gt;kubectl top&lt;/code&gt;. For full observability, deploy the &lt;strong&gt;kube-prometheus-stack&lt;/strong&gt; (Prometheus + Alertmanager + Grafana) or a managed equivalent.&lt;/p&gt;

&lt;p&gt;Key scrape targets:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;kube-state-metrics&lt;/code&gt; — Deployment replicas, Pod status, resource requests/limits, PVC state.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;node-exporter&lt;/code&gt; — node-level CPU, memory, disk, network.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;cadvisor&lt;/code&gt; — per-container CPU and memory (built into kubelet).&lt;/li&gt;
&lt;li&gt;Your application's &lt;code&gt;/metrics&lt;/code&gt; endpoint (expose Prometheus format).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Understanding Prometheus query syntax is essential for writing alerts and dashboards. Use the &lt;a href="https://opscanopy.com/promql-explainer/" rel="noopener noreferrer"&gt;PromQL Explainer&lt;/a&gt; to translate complex queries into plain English. When building scrape configurations and relabelling rules, the &lt;a href="https://opscanopy.com/prometheus-relabel-tester/" rel="noopener noreferrer"&gt;Prometheus Relabel Tester&lt;/a&gt; lets you test rules against live label sets without touching production.&lt;/p&gt;

&lt;h3&gt;
  
  
  Events
&lt;/h3&gt;

&lt;p&gt;Kubernetes Events are the first place to look when a Pod is not starting correctly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get events &lt;span class="nt"&gt;-n&lt;/span&gt; production &lt;span class="nt"&gt;--sort-by&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'.lastTimestamp'&lt;/span&gt;
kubectl get events &lt;span class="nt"&gt;-n&lt;/span&gt; production &lt;span class="nt"&gt;--field-selector&lt;/span&gt; &lt;span class="nv"&gt;reason&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;OOMKilled
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  kubectl Essentials
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;What It Does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;kubectl get pods -n &amp;lt;ns&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List Pods in a namespace&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;kubectl get pods -A&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List Pods in all namespaces&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;kubectl describe pod &amp;lt;name&amp;gt; -n &amp;lt;ns&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Full details, events, conditions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;kubectl logs &amp;lt;pod&amp;gt; -n &amp;lt;ns&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Stdout/stderr from the main container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;kubectl logs &amp;lt;pod&amp;gt; -c &amp;lt;container&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Logs from a specific container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;kubectl logs &amp;lt;pod&amp;gt; --previous&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Logs from the last crashed container instance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;kubectl exec -it &amp;lt;pod&amp;gt; -- bash&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Interactive shell in a running container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;kubectl exec &amp;lt;pod&amp;gt; -- &amp;lt;cmd&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run a one-off command&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;kubectl apply -f &amp;lt;file&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Declaratively create or update resources&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;kubectl delete -f &amp;lt;file&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Delete resources defined in a manifest&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;kubectl delete pod &amp;lt;name&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Force-delete a single Pod&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;kubectl scale deployment &amp;lt;name&amp;gt; --replicas=N&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Scale a Deployment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;kubectl rollout status deployment/&amp;lt;name&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Watch rollout progress&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;kubectl rollout undo deployment/&amp;lt;name&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Roll back to previous version&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;kubectl rollout history deployment/&amp;lt;name&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;View revision history&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;kubectl set image deployment/&amp;lt;name&amp;gt; &amp;lt;c&amp;gt;=&amp;lt;img&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Update a container image&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;kubectl port-forward svc/&amp;lt;name&amp;gt; 8080:80&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Forward a local port to a Service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;kubectl port-forward pod/&amp;lt;name&amp;gt; 8080:8080&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Forward a local port to a Pod&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;kubectl top pods -n &amp;lt;ns&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Current CPU/memory usage (requires metrics-server)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;kubectl top nodes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Node CPU/memory usage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;kubectl get events -n &amp;lt;ns&amp;gt; --sort-by=.lastTimestamp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Recent cluster events&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;kubectl explain deployment.spec.strategy&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Inline API documentation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;kubectl diff -f &amp;lt;file&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Preview what &lt;code&gt;apply&lt;/code&gt; would change&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;kubectl get all -n &amp;lt;ns&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;All common resources in a namespace&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;kubectl label pod &amp;lt;name&amp;gt; key=value&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Add or update a label&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;kubectl annotate pod &amp;lt;name&amp;gt; key=value&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Add or update an annotation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;kubectl config get-contexts&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List available kubeconfig contexts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;kubectl config use-context &amp;lt;context&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Switch cluster context&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Install &lt;code&gt;kubectl&lt;/code&gt; plugins via &lt;code&gt;krew&lt;/code&gt; (the kubectl plugin manager). Useful plugins include &lt;code&gt;kubectl-neat&lt;/code&gt; (clean up &lt;code&gt;get -o yaml&lt;/code&gt; output), &lt;code&gt;kubectl-ctx&lt;/code&gt; and &lt;code&gt;kubectl-ns&lt;/code&gt; (fast context/namespace switching), and &lt;code&gt;kubectl-tree&lt;/code&gt; (visualise owner references).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;h3&gt;
  
  
  CrashLoopBackOff
&lt;/h3&gt;

&lt;p&gt;A container keeps crashing and Kubernetes keeps restarting it with exponential backoff.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Diagnose:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# See the current state and restart count&lt;/span&gt;
kubectl get pod &amp;lt;name&amp;gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &amp;lt;ns&amp;gt;

&lt;span class="c"&gt;# Check events for the Pod&lt;/span&gt;
kubectl describe pod &amp;lt;name&amp;gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &amp;lt;ns&amp;gt;

&lt;span class="c"&gt;# Check logs from the current or previous instance&lt;/span&gt;
kubectl logs &amp;lt;name&amp;gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &amp;lt;ns&amp;gt;
kubectl logs &amp;lt;name&amp;gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &amp;lt;ns&amp;gt; &lt;span class="nt"&gt;--previous&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Common causes:&lt;/strong&gt; application throws an unhandled exception at startup; missing environment variable or config file; entrypoint command is wrong; dependency (database, cache) not reachable.&lt;/p&gt;

&lt;h3&gt;
  
  
  ImagePullBackOff / ErrImagePull
&lt;/h3&gt;

&lt;p&gt;The container image cannot be pulled.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Diagnose:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl describe pod &amp;lt;name&amp;gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &amp;lt;ns&amp;gt;
&lt;span class="c"&gt;# Look for events like: Failed to pull image ... unauthorized&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Common causes:&lt;/strong&gt; image name or tag is wrong; registry is private and no &lt;code&gt;imagePullSecret&lt;/code&gt; is configured; credentials in the pull secret have expired; rate-limit on Docker Hub.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix a missing imagePullSecret:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl create secret docker-registry regcred &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--docker-server&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;registry.example.com &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--docker-username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;user&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--docker-password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;token&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-n&lt;/span&gt; production
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then reference it in the Pod spec:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;imagePullSecrets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;regcred&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Pending Pods
&lt;/h3&gt;

&lt;p&gt;A Pod stays in &lt;code&gt;Pending&lt;/code&gt; and never starts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Diagnose:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl describe pod &amp;lt;name&amp;gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &amp;lt;ns&amp;gt;
&lt;span class="c"&gt;# Look for events under "Events:" — scheduler messages explain why&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Common causes:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Insufficient resources&lt;/strong&gt; — no node has enough CPU or memory to satisfy the requests. Check &lt;code&gt;kubectl top nodes&lt;/code&gt; and &lt;code&gt;kubectl describe nodes&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Taint/toleration mismatch&lt;/strong&gt; — the target nodes are tainted but the Pod has no matching toleration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NodeSelector or affinity&lt;/strong&gt; — the Pod requires specific labels on the node that no current node has.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PVC not bound&lt;/strong&gt; — a &lt;code&gt;PersistentVolumeClaim&lt;/code&gt; is still &lt;code&gt;Pending&lt;/code&gt; (waiting for a PV).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  OOMKilled
&lt;/h3&gt;

&lt;p&gt;A container was killed by the kernel OOM killer because it exceeded its memory limit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Diagnose:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get pod &amp;lt;name&amp;gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &amp;lt;ns&amp;gt;
&lt;span class="c"&gt;# Look for: OOMKilled in the REASON column&lt;/span&gt;

kubectl describe pod &amp;lt;name&amp;gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &amp;lt;ns&amp;gt;
&lt;span class="c"&gt;# Look for: Last State: Terminated Reason: OOMKilled&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Increase the memory limit (or request, to get a Guaranteed QoS). Profile the application's actual memory use under load. Use the &lt;a href="https://opscanopy.com/kubernetes-resource-calculator/" rel="noopener noreferrer"&gt;Kubernetes Resource Calculator&lt;/a&gt; to size requests and limits based on observed usage.&lt;/p&gt;

&lt;h3&gt;
  
  
  Debugging Toolkit
&lt;/h3&gt;

&lt;p&gt;When &lt;code&gt;kubectl logs&lt;/code&gt; is not enough:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Open a shell in the running container&lt;/span&gt;
kubectl &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; &amp;lt;pod&amp;gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &amp;lt;ns&amp;gt; &lt;span class="nt"&gt;--&lt;/span&gt; sh

&lt;span class="c"&gt;# Run a temporary debug Pod on a specific node&lt;/span&gt;
kubectl debug node/&amp;lt;node-name&amp;gt; &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;--image&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ubuntu:22.04

&lt;span class="c"&gt;# Attach an ephemeral debug container to a running Pod (K8s 1.23+)&lt;/span&gt;
kubectl debug &lt;span class="nt"&gt;-it&lt;/span&gt; &amp;lt;pod&amp;gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &amp;lt;ns&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--image&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;busybox:1.36 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--target&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;container-name&amp;gt;

&lt;span class="c"&gt;# Copy a file from a Pod to local&lt;/span&gt;
kubectl &lt;span class="nb"&gt;cp&lt;/span&gt; &amp;lt;ns&amp;gt;/&amp;lt;pod&amp;gt;:/path/to/file ./local-file
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Common Kubernetes Interview Questions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Q: What is the role of the etcd in a Kubernetes cluster?&lt;/strong&gt;&lt;br&gt;
A: etcd is the cluster's source of truth — a distributed, consistent key-value store where all cluster state (objects, configs, secrets) is persisted. If etcd is lost without a backup, the cluster's configuration is lost. The API server is the only component that reads and writes etcd directly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: How does a Kubernetes Service discover which Pods to send traffic to?&lt;/strong&gt;&lt;br&gt;
A: Services use label selectors to identify target Pods. The Endpoints controller (or EndpointSlice controller in newer clusters) watches for Pods matching the selector and maintains an Endpoints/EndpointSlice object. &lt;code&gt;kube-proxy&lt;/code&gt; reads these to program iptables/IPVS forwarding rules.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: What is the difference between a StatefulSet and a Deployment?&lt;/strong&gt;&lt;br&gt;
A: A Deployment is for stateless workloads — Pods are interchangeable. A StatefulSet is for stateful applications (databases, queues): it gives each Pod a stable, ordered hostname (&lt;code&gt;pod-0&lt;/code&gt;, &lt;code&gt;pod-1&lt;/code&gt;), stable storage via PVCs that survive Pod rescheduling, and ordered, graceful start/stop.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: What is a DaemonSet?&lt;/strong&gt;&lt;br&gt;
A: A DaemonSet ensures that one copy of a Pod runs on every node (or a subset via nodeSelector/tolerations). Common uses: log collectors (Fluent Bit), monitoring agents (node-exporter), CNI plugins, security agents.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: How does Kubernetes handle persistent storage?&lt;/strong&gt;&lt;br&gt;
A: Via the PersistentVolume (PV) / PersistentVolumeClaim (PVC) abstraction. A PVC is a user's request for storage (size, access mode). Kubernetes binds it to a matching PV (a piece of actual storage — EBS volume, NFS share, etc.) either statically or dynamically via a StorageClass and a CSI driver.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: What is a Kubernetes Namespace and when should you use multiple namespaces?&lt;/strong&gt;&lt;br&gt;
A: A Namespace is a virtual cluster within a cluster. Use separate namespaces for teams, environments (dev/staging/production on the same cluster), or application boundaries. Apply ResourceQuotas and RBAC per namespace to enforce isolation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: What is a Taint and Toleration?&lt;/strong&gt;&lt;br&gt;
A: Taints are set on nodes to repel Pods. &lt;code&gt;kubectl taint nodes node1 key=value:NoSchedule&lt;/code&gt; prevents any Pod without a matching Toleration from being scheduled on that node. Used to dedicate nodes for specific workloads (GPU nodes, spot nodes).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: What happens when you run &lt;code&gt;kubectl apply -f deployment.yaml&lt;/code&gt;?&lt;/strong&gt;&lt;br&gt;
A: kubectl sends the manifest to the API server, which validates it and merges it with the existing object (three-way merge using the &lt;code&gt;last-applied-configuration&lt;/code&gt; annotation). If the Deployment's Pod template changed, the Deployment controller creates a new ReplicaSet and performs a rolling update.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: What is the difference between &lt;code&gt;kubectl apply&lt;/code&gt; and &lt;code&gt;kubectl create&lt;/code&gt;?&lt;/strong&gt;&lt;br&gt;
A: &lt;code&gt;create&lt;/code&gt; is imperative — it fails if the object already exists. &lt;code&gt;apply&lt;/code&gt; is declarative — it creates the object if missing or updates it if it exists, using a three-way merge. Prefer &lt;code&gt;apply&lt;/code&gt; for GitOps and CI/CD pipelines.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: What is a Kubernetes Job vs a CronJob?&lt;/strong&gt;&lt;br&gt;
A: A Job creates one or more Pods and tracks their completion. It retries failed Pods until the desired completions count is reached, then the Job is marked complete. A CronJob schedules Jobs on a cron expression, creating a new Job object on each trigger.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Prefer a day-by-day path? This is covered in &lt;a href="https://opscanopy.com/mission-90/" rel="noopener noreferrer"&gt;&lt;strong&gt;Mission 90 Days 66–74&lt;/strong&gt;&lt;/a&gt; — a free 90-day guided DevOps program with browser terminal missions.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;Coming from containers? See &lt;a href="https://opscanopy.com/learn/guides/docker-for-devops/" rel="noopener noreferrer"&gt;Docker for DevOps&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Running on AWS (EKS)? See &lt;a href="https://opscanopy.com/learn/guides/aws-for-devops-engineers/" rel="noopener noreferrer"&gt;AWS for DevOps Engineers&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>k8s</category>
      <category>devops</category>
      <category>containers</category>
    </item>
    <item>
      <title>Docker Interview Prep: Scenario-Based Questions &amp; Answers for DevOps</title>
      <dc:creator>opscanopy</dc:creator>
      <pubDate>Mon, 17 Aug 2026 15:36:01 +0000</pubDate>
      <link>https://dev.to/opscanopy/docker-interview-prep-scenario-based-questions-answers-for-devops-4led</link>
      <guid>https://dev.to/opscanopy/docker-interview-prep-scenario-based-questions-answers-for-devops-4led</guid>
      <description>&lt;p&gt;This guide is built for DevOps interview prep. It covers the ten core Docker topics that come up repeatedly — from why containers exist through to a production-grade troubleshooting playbook — using scenario-based questions, clear answers, worked examples, and the gotchas interviewers love to probe. Every command is real and runnable on Ubuntu 24.04 with a current Docker Engine. Before diving in, bookmark the &lt;a href="https://opscanopy.com/learn/roadmaps/docker/" rel="noopener noreferrer"&gt;Docker roadmap&lt;/a&gt; to see how these topics fit into a full learning path.&lt;/p&gt;

&lt;p&gt;Study the concepts first in &lt;a href="https://opscanopy.com/learn/guides/docker-for-devops/" rel="noopener noreferrer"&gt;Docker for DevOps&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Containers Exist
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Interview scenario:&lt;/strong&gt; "You shipped a React app and it works on your machine but breaks in production. How do you explain what went wrong, and what's the architectural fix?"&lt;/p&gt;

&lt;p&gt;Your laptop has Node 20, a specific OpenSSL version, certain environment variables, and a particular Ubuntu setup. The production server has Node 18, a different libc, and missing env vars. Same code, different behaviour. The bug is not in your code — it is in the &lt;em&gt;environment&lt;/em&gt; around your code.&lt;/p&gt;

&lt;p&gt;Traditionally, teams fixed this with long setup documents, install scripts, and "please match these exact versions" checklists. It never fully worked because the operating system, system libraries, and dependencies drifted between every developer's machine and every server. Containers solve this by packaging your app &lt;strong&gt;together with its entire runtime environment&lt;/strong&gt; — the right Node version, libraries, files, and config — into one portable unit that runs identically everywhere.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Think of a shipping container. Before standardised containers, dock workers loaded ships piece by piece — barrels, boxes, sacks — and every port handled it differently, slowly, and unreliably. The steel shipping container changed the world: one standard box, the same shape everywhere, that any crane, truck, or ship can handle without caring what's inside. Docker does the same for software.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Virtual Machines vs Containers
&lt;/h3&gt;

&lt;p&gt;Before containers, the common way to isolate apps was the Virtual Machine (VM). A VM emulates an entire computer — including a full guest operating system — on top of a hypervisor. That gives strong isolation, but it is heavy: every VM carries its own multi-gigabyte OS.&lt;/p&gt;

&lt;p&gt;Containers take a smarter approach. They &lt;strong&gt;share the host's OS kernel&lt;/strong&gt; and only package the app plus its dependencies, making them tiny and fast.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Architecture comparison:&lt;/strong&gt; In a VM setup, each application runs inside its own Guest OS on a Hypervisor, all sitting on Host OS + Hardware. Every VM is gigabytes in size. In a container setup, applications each have their own bins/libs but share a single Docker Engine on top of a shared Host OS Kernel. No guest OS means containers are megabytes, not gigabytes.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Virtual Machine&lt;/th&gt;
&lt;th&gt;Container&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Boot time&lt;/td&gt;
&lt;td&gt;Minutes (full OS boot)&lt;/td&gt;
&lt;td&gt;Seconds or less&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Size&lt;/td&gt;
&lt;td&gt;Gigabytes (includes guest OS)&lt;/td&gt;
&lt;td&gt;Megabytes (app + deps only)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Isolation&lt;/td&gt;
&lt;td&gt;Strong — full hardware-level isolation via hypervisor&lt;/td&gt;
&lt;td&gt;Process-level isolation via kernel namespaces &amp;amp; cgroups&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Resource use&lt;/td&gt;
&lt;td&gt;Heavy — each VM runs its own OS&lt;/td&gt;
&lt;td&gt;Lightweight — shares host kernel&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OS kernel&lt;/td&gt;
&lt;td&gt;Each VM has its own kernel&lt;/td&gt;
&lt;td&gt;All containers share the host kernel&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Portability&lt;/td&gt;
&lt;td&gt;Portable but bulky to move&lt;/td&gt;
&lt;td&gt;Highly portable — small images, run anywhere Docker runs&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; A team running 10 microservices as VMs would need 10 guest operating systems eating ~20 GB of RAM with minutes to spin up. As containers on a single host, those same 10 services share one kernel, boot in seconds, and fit comfortably in a fraction of the memory.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  What Docker Actually Solves
&lt;/h3&gt;

&lt;p&gt;Docker is not the same thing as "containers" — Linux had the underlying technology (namespaces and cgroups) for years. What Docker did was make containers &lt;strong&gt;easy&lt;/strong&gt;: a simple way to define an environment (the Dockerfile), a standard format to package it (the image), a registry to share it (Docker Hub), and a clean CLI to run it. Consistency from laptop to CI to production, fast startup, and efficient resource use — that is the Docker promise.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; "What's the difference between a VM and a container?" gets asked in almost every DevOps interview. Nail the one-liner: &lt;em&gt;A VM virtualizes hardware and runs a full guest OS on a hypervisor; a container virtualizes the OS and shares the host kernel, packaging only the app and its dependencies.&lt;/em&gt; Then add the consequences — containers are smaller, boot faster, and use fewer resources, while VMs give stronger isolation. Bonus points: they are complementary — you often run containers &lt;em&gt;inside&lt;/em&gt; VMs in the cloud.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Core Concepts: Images, Containers, Registry, and the Engine
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Images vs Containers
&lt;/h3&gt;

&lt;p&gt;This is the single most important distinction in Docker, and it maps perfectly onto something any developer already knows: &lt;strong&gt;class vs object&lt;/strong&gt;. An &lt;strong&gt;image&lt;/strong&gt; is a read-only blueprint — like a class definition. A &lt;strong&gt;container&lt;/strong&gt; is a running instance of that image — like an object created with &lt;code&gt;new&lt;/code&gt;. From one image you can spin up many containers, just like one class can produce many objects, each with its own state.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; An image is the recipe; a container is the actual dish you cooked from it. One recipe, many plates. The recipe never changes when you cook — and your image stays read-only while each container runs its own live copy.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Image&lt;/th&gt;
&lt;th&gt;Container&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;What it is&lt;/td&gt;
&lt;td&gt;Read-only template / blueprint&lt;/td&gt;
&lt;td&gt;Running instance of an image&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;State&lt;/td&gt;
&lt;td&gt;Immutable, static&lt;/td&gt;
&lt;td&gt;Live, has writable layer &amp;amp; runtime state&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Analogy&lt;/td&gt;
&lt;td&gt;Class / recipe&lt;/td&gt;
&lt;td&gt;Object / cooked dish&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lifecycle&lt;/td&gt;
&lt;td&gt;Built once, stored, reused&lt;/td&gt;
&lt;td&gt;Created, started, stopped, removed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Quantity&lt;/td&gt;
&lt;td&gt;One image&lt;/td&gt;
&lt;td&gt;Many containers from that one image&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;/p&gt;
One immutable image produces many independent containers, each adding its own ephemeral writable layer on top.





&lt;h3&gt;
  
  
  The Docker Engine Model
&lt;/h3&gt;

&lt;p&gt;"Docker" is actually several pieces working together. Understanding this chain is valuable in interviews.&lt;/p&gt;

&lt;p&gt;The flow is: &lt;strong&gt;CLI Client&lt;/strong&gt; → (REST API) → &lt;strong&gt;Docker Daemon (dockerd)&lt;/strong&gt; → (pull/push) → &lt;strong&gt;Registry (Docker Hub / ECR)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CLI Client&lt;/strong&gt; — the &lt;code&gt;docker&lt;/code&gt; command you type. It is a thin client that sends requests to the daemon over a REST API (Unix socket). It does no container work itself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Docker Daemon (dockerd)&lt;/strong&gt; — the background service that does the real work: building images, managing containers, networks, and volumes. It delegates container execution to containerd/runc.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Images&lt;/strong&gt; — read-only templates stored locally by the daemon. If a requested image is absent, the daemon pulls it from the registry. Each image is a stack of layers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Containers&lt;/strong&gt; — running instances the daemon creates from images by adding a writable layer, then launching the process via containerd → runc with kernel namespaces and cgroups.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Registry&lt;/strong&gt; — remote image store (Docker Hub, ECR, GHCR). The daemon &lt;code&gt;pull&lt;/code&gt;s images down when missing and &lt;code&gt;push&lt;/code&gt;es built images up to share.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Image Layers and the Union Filesystem
&lt;/h3&gt;

&lt;p&gt;A Docker image is not one solid blob — it is built from &lt;strong&gt;stacked layers&lt;/strong&gt;. Each instruction in a Dockerfile (install a package, copy files, set config) creates a new read-only layer on top of the previous one. Docker uses a &lt;strong&gt;union filesystem&lt;/strong&gt; to merge these layers into a single view that the container sees as one normal filesystem.&lt;/p&gt;

&lt;p&gt;Two big wins come from this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Caching&lt;/strong&gt; — if a layer hasn't changed, Docker reuses the cached version instead of rebuilding it, so builds are fast.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sharing&lt;/strong&gt; — if ten images all start &lt;code&gt;FROM ubuntu:24.04&lt;/code&gt;, that base layer is stored on disk only once and shared between them.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When a container runs, Docker adds a thin &lt;strong&gt;writable layer&lt;/strong&gt; on top — all runtime changes live there, and the underlying image layers stay untouched.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layer stack for a Node app (bottom to top):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM node:20-alpine        ← base layer (read-only, cached)
COPY package.json ./       ← layer: package.json
RUN npm ci                 ← layer: node_modules (slow, cached when deps unchanged)
COPY . .                   ← layer: app source (changes often)
CMD ["node","server.js"]   ← metadata: start command
                           ← writable container layer (top, ephemeral)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you change a layer, Docker must rebuild that layer &lt;strong&gt;and every layer above it&lt;/strong&gt;. Layers below it stay cached. This is why dependencies go early and source goes last.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Order your Dockerfile from least-changing to most-changing. Put &lt;code&gt;COPY package.json&lt;/code&gt; and &lt;code&gt;npm install&lt;/code&gt; &lt;em&gt;before&lt;/em&gt; &lt;code&gt;COPY . .&lt;/code&gt; — that way, changing your source code doesn't bust the cached dependency layer, and rebuilds stay fast.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Registry and Docker Hub
&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;registry&lt;/strong&gt; is a storage and distribution system for images — think of it as "GitHub for Docker images." &lt;strong&gt;Docker Hub&lt;/strong&gt; is the default public registry, hosting official images like &lt;code&gt;nginx&lt;/code&gt;, &lt;code&gt;node&lt;/code&gt;, &lt;code&gt;postgres&lt;/code&gt;, and &lt;code&gt;ubuntu&lt;/code&gt;. You &lt;em&gt;pull&lt;/em&gt; images down to run them and &lt;em&gt;push&lt;/em&gt; your own images up to share them. Companies usually run private registries too (AWS ECR, GitHub Container Registry, Harbor) for internal images.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; When you run &lt;code&gt;docker pull nginx&lt;/code&gt;, Docker contacts Docker Hub, finds the official &lt;code&gt;nginx&lt;/code&gt; image, and downloads it layer by layer. Each layer is pulled separately — and any layers already present from a previous pull are skipped. That is the layer-sharing system saving bandwidth and disk in real time.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run nginx
&lt;span class="c"&gt;# CLI (client) ── REST API ──&amp;gt; dockerd (daemon)&lt;/span&gt;
&lt;span class="c"&gt;#                                 │&lt;/span&gt;
&lt;span class="c"&gt;#                  pulls image from registry if missing&lt;/span&gt;
&lt;span class="c"&gt;#                                 │&lt;/span&gt;
&lt;span class="c"&gt;#                              containerd ──&amp;gt; runc ──&amp;gt; container process&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; "Walk me through what happens when you run &lt;code&gt;docker run nginx&lt;/code&gt;." Strong answer: (1) the CLI sends the request to the Docker daemon via the API; (2) the daemon checks for the &lt;code&gt;nginx&lt;/code&gt; image locally; (3) if missing, it pulls the image layers from the registry; (4) it creates a new container by stacking a writable layer on the image; (5) it hands off to containerd/runc, which sets up namespaces and cgroups; (6) the container's main process starts. Mentioning the client–daemon split and containerd/runc shows real depth.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Essential Docker CLI
&lt;/h2&gt;

&lt;p&gt;These are the commands you will type every single day. Each one below has a real example and a clear use case.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Container lifecycle:&lt;/strong&gt; Created → Running → Stopped → Removed, driven by &lt;code&gt;docker create&lt;/code&gt; → &lt;code&gt;docker start&lt;/code&gt;/&lt;code&gt;run&lt;/code&gt; → &lt;code&gt;docker stop&lt;/code&gt; → &lt;code&gt;docker rm&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  docker run — create and start a container
&lt;/h3&gt;

&lt;p&gt;The workhorse. Key flags: &lt;code&gt;-d&lt;/code&gt; (detached/background), &lt;code&gt;-p host:container&lt;/code&gt; (publish a port), &lt;code&gt;--name&lt;/code&gt; (friendly name), &lt;code&gt;-it&lt;/code&gt; (interactive terminal), &lt;code&gt;--rm&lt;/code&gt; (auto-delete on exit), &lt;code&gt;-e&lt;/code&gt; (environment variable), &lt;code&gt;-v&lt;/code&gt; (mount a volume).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:80 &lt;span class="nt"&gt;--name&lt;/span&gt; web nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run nginx in the background, mapping host port 8080 to container port 80. Open &lt;code&gt;http://localhost:8080&lt;/code&gt; to see it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;--rm&lt;/span&gt; ubuntu:24.04 bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Get an interactive shell inside a throwaway Ubuntu container that auto-deletes the moment you exit — perfect for quick experiments.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; db &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;secret &lt;span class="nt"&gt;-v&lt;/span&gt; pgdata:/var/lib/postgresql/data &lt;span class="nt"&gt;-p&lt;/span&gt; 5432:5432 postgres:16
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run Postgres with an env var for the password and a named volume &lt;code&gt;pgdata&lt;/code&gt; so your database survives even after the container is removed.&lt;/p&gt;

&lt;h3&gt;
  
  
  docker ps — list containers
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker ps
docker ps &lt;span class="nt"&gt;-a&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;docker ps&lt;/code&gt; shows only running containers; &lt;code&gt;docker ps -a&lt;/code&gt; shows all containers including stopped/exited ones — essential for debugging why something died.&lt;/p&gt;

&lt;h3&gt;
  
  
  docker images — list local images
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker images
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See every image stored locally, with its repository, tag, image ID, and size.&lt;/p&gt;

&lt;h3&gt;
  
  
  docker pull — download an image
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker pull node:20-alpine
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Grab a specific image from the registry ahead of time, so the first &lt;code&gt;docker run&lt;/code&gt; doesn't have to wait on the download.&lt;/p&gt;

&lt;h3&gt;
  
  
  docker exec — run a command in a running container
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; web bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Jump inside a &lt;em&gt;running&lt;/em&gt; container to inspect files, check config, or debug live. &lt;code&gt;-it&lt;/code&gt; gives you an interactive shell. (If the image lacks bash, use &lt;code&gt;sh&lt;/code&gt;.)&lt;/p&gt;

&lt;h3&gt;
  
  
  docker logs — view container output
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker logs &lt;span class="nt"&gt;-f&lt;/span&gt; web
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Stream the logs (stdout/stderr) of the &lt;code&gt;web&lt;/code&gt; container live with &lt;code&gt;-f&lt;/code&gt; (follow) — your go-to for watching what an app is doing in real time.&lt;/p&gt;

&lt;h3&gt;
  
  
  docker stop / start — control lifecycle
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker stop web
docker start web
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Gracefully stop a running container (it gets a chance to shut down cleanly) and later start it back up without recreating it.&lt;/p&gt;

&lt;h3&gt;
  
  
  docker rm / rmi — remove containers and images
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;rm &lt;/span&gt;web
docker rmi nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;docker rm&lt;/code&gt; deletes a stopped container; &lt;code&gt;docker rmi&lt;/code&gt; deletes an image you no longer need to reclaim disk space.&lt;/p&gt;

&lt;h3&gt;
  
  
  docker build — build an image from a Dockerfile
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; myapp:1.0 &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Build an image named &lt;code&gt;myapp&lt;/code&gt; tagged &lt;code&gt;1.0&lt;/code&gt; from the Dockerfile in the current directory (the &lt;code&gt;.&lt;/code&gt; is the build context).&lt;/p&gt;

&lt;h3&gt;
  
  
  docker tag — give an image another name/tag
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker tag myapp:1.0 pushkar/myapp:1.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Re-tag your local image with your Docker Hub username (and/or registry) so it's ready to push to the right repository.&lt;/p&gt;

&lt;h3&gt;
  
  
  docker push — upload an image to a registry
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker login
docker push pushkar/myapp:1.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After &lt;code&gt;docker login&lt;/code&gt;, push your tagged image to Docker Hub so teammates or your servers can pull and run it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Quick Reference
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;th&gt;Most-used form&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;run&lt;/td&gt;
&lt;td&gt;Create + start a container&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker run -d -p 8080:80 --name web nginx&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ps&lt;/td&gt;
&lt;td&gt;List containers&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker ps -a&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;images&lt;/td&gt;
&lt;td&gt;List local images&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker images&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;pull&lt;/td&gt;
&lt;td&gt;Download an image&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker pull node:20-alpine&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;exec&lt;/td&gt;
&lt;td&gt;Run a command in a live container&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker exec -it web bash&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;logs&lt;/td&gt;
&lt;td&gt;View container output&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker logs -f web&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;stop / start&lt;/td&gt;
&lt;td&gt;Control a container's lifecycle&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker stop web&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rm / rmi&lt;/td&gt;
&lt;td&gt;Remove container / image&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;docker rm web&lt;/code&gt; · &lt;code&gt;docker rmi nginx&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;build&lt;/td&gt;
&lt;td&gt;Build image from Dockerfile&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker build -t myapp:1.0 .&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;tag&lt;/td&gt;
&lt;td&gt;Re-name/tag an image&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker tag myapp:1.0 pushkar/myapp:1.0&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;push&lt;/td&gt;
&lt;td&gt;Upload image to registry&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker push pushkar/myapp:1.0&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; &lt;code&gt;docker run&lt;/code&gt; is really three steps in one: &lt;strong&gt;pull&lt;/strong&gt; (if the image isn't local) + &lt;strong&gt;create&lt;/strong&gt; (build the container from the image) + &lt;strong&gt;start&lt;/strong&gt; (launch its main process). &lt;code&gt;docker create&lt;/code&gt; + &lt;code&gt;docker start&lt;/code&gt; do separately what &lt;code&gt;run&lt;/code&gt; does together.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; &lt;code&gt;docker rm -f&lt;/code&gt; kills and removes a running container with no graceful shutdown — fine for test junk, dangerous for anything with unsaved state. &lt;code&gt;docker system prune -a&lt;/code&gt; deletes all stopped containers, unused networks, and &lt;em&gt;all&lt;/em&gt; images not currently in use. Read what it's about to delete before you confirm.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; A classic gotcha: "What's the difference between &lt;code&gt;docker stop&lt;/code&gt; and &lt;code&gt;docker kill&lt;/code&gt;?" — &lt;code&gt;stop&lt;/code&gt; sends &lt;code&gt;SIGTERM&lt;/code&gt; first (graceful, with a grace period) then &lt;code&gt;SIGKILL&lt;/code&gt;; &lt;code&gt;kill&lt;/code&gt; sends &lt;code&gt;SIGKILL&lt;/code&gt; immediately. Another favourite: "Difference between &lt;code&gt;EXPOSE&lt;/code&gt; in a Dockerfile and &lt;code&gt;-p&lt;/code&gt; at runtime?" — &lt;code&gt;EXPOSE&lt;/code&gt; only documents the port; &lt;code&gt;-p&lt;/code&gt; actually publishes it to the host.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Dockerfile Deep-Dive
&lt;/h2&gt;

&lt;p&gt;A Dockerfile is a recipe. Each line is one instruction that tells Docker how to assemble your image, layer by layer. Think of it like your build pipeline written as plain text: install the runtime, copy your code, install deps, define how the app starts.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; A Dockerfile is a recipe card and the image is the prepped meal kit. The &lt;em&gt;container&lt;/em&gt; is when you actually cook and eat it. You can build the kit once and cook it on a thousand machines, and it tastes identical every time.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  FROM — the base layer
&lt;/h3&gt;

&lt;p&gt;Every Dockerfile starts with &lt;code&gt;FROM&lt;/code&gt;. It sets the base image you build on top of. Pin a specific version tag; never rely on &lt;code&gt;latest&lt;/code&gt; in production because it silently changes under you.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:20-alpine&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;node:20&lt;/code&gt; is the version and &lt;code&gt;-alpine&lt;/code&gt; is the variant (a tiny Linux distro, ~5 MB base).&lt;/p&gt;

&lt;h3&gt;
  
  
  RUN — execute commands at build time
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;RUN&lt;/code&gt; runs a command &lt;em&gt;while building the image&lt;/em&gt; and bakes the result into a new layer. Use it to install packages, create directories, or run build steps. Each &lt;code&gt;RUN&lt;/code&gt; creates a layer, so chain related commands with &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; to keep layer count down.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;RUN &lt;/span&gt;apk add &lt;span class="nt"&gt;--no-cache&lt;/span&gt; python3 make g++ &lt;span class="se"&gt;\
&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; some-cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  COPY — bring files into the image
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;COPY&lt;/code&gt; copies files/folders from your build context (your project directory) into the image. This is how your source code gets in.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package.json package-lock.json ./&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ADD — COPY's older, fancier cousin
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;ADD&lt;/code&gt; does everything &lt;code&gt;COPY&lt;/code&gt; does, plus two extras: it can fetch a remote URL, and it auto-extracts local tar archives. Those extras make it less predictable, so the rule of thumb is: &lt;strong&gt;use COPY unless you specifically need ADD's tar extraction&lt;/strong&gt;.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Behaviour&lt;/th&gt;
&lt;th&gt;COPY&lt;/th&gt;
&lt;th&gt;ADD&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Copy local files/dirs&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Auto-extract local &lt;code&gt;.tar&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Download from URL&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes (discouraged)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Predictable / explicit&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No (magic behaviour)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Recommended default&lt;/td&gt;
&lt;td&gt;Use this&lt;/td&gt;
&lt;td&gt;Only for tar extraction&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  WORKDIR — set the working directory
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;WORKDIR&lt;/code&gt; sets the current directory for every following instruction (&lt;code&gt;RUN&lt;/code&gt;, &lt;code&gt;CMD&lt;/code&gt;, &lt;code&gt;COPY&lt;/code&gt;, etc.) and creates it if it doesn't exist. Always use it instead of &lt;code&gt;RUN cd /app&lt;/code&gt; — &lt;code&gt;cd&lt;/code&gt; doesn't persist across layers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  CMD vs ENTRYPOINT — how the container starts
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Interview scenario:&lt;/strong&gt; "What's the difference between CMD and ENTRYPOINT?" This is a guaranteed question.&lt;/p&gt;

&lt;p&gt;Both define what runs when the container starts, but they play different roles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ENTRYPOINT&lt;/strong&gt; is the fixed command — the thing this container &lt;em&gt;is&lt;/em&gt;. It needs &lt;code&gt;--entrypoint&lt;/code&gt; to override at runtime.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CMD&lt;/strong&gt; provides default arguments (or a default command) that are &lt;em&gt;easily overridden&lt;/em&gt; at &lt;code&gt;docker run&lt;/code&gt; time. When both are present, CMD's values are appended as arguments to ENTRYPOINT.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Exec form vs shell form:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Exec form&lt;/strong&gt; (JSON array): &lt;code&gt;CMD ["node", "server.js"]&lt;/code&gt; — runs the binary directly, no shell. This is the correct form: signals like &lt;code&gt;SIGTERM&lt;/code&gt; reach your process so it can shut down cleanly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shell form&lt;/strong&gt;: &lt;code&gt;CMD node server.js&lt;/code&gt; — wraps the command in &lt;code&gt;/bin/sh -c&lt;/code&gt;. The shell becomes PID 1 and may swallow signals, so your app doesn't get the stop signal. Avoid for the main process.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# They combine: final command = ENTRYPOINT + CMD&lt;/span&gt;
&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["node"]&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["server.js"]&lt;/span&gt;
&lt;span class="c"&gt;# docker run myimg            -&amp;gt; node server.js&lt;/span&gt;
&lt;span class="c"&gt;# docker run myimg other.js   -&amp;gt; node other.js   (CMD overridden)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;CMD&lt;/th&gt;
&lt;th&gt;ENTRYPOINT&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Purpose&lt;/td&gt;
&lt;td&gt;Default command / args&lt;/td&gt;
&lt;td&gt;Fixed executable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Overridable at &lt;code&gt;docker run&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Yes — just pass new args&lt;/td&gt;
&lt;td&gt;No (needs &lt;code&gt;--entrypoint&lt;/code&gt; flag)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;When both exist&lt;/td&gt;
&lt;td&gt;Becomes arguments to ENTRYPOINT&lt;/td&gt;
&lt;td&gt;Stays as the executable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Typical use&lt;/td&gt;
&lt;td&gt;App that users may re-run with different args&lt;/td&gt;
&lt;td&gt;Wrapper / tool where the binary is fixed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Can appear multiple times&lt;/td&gt;
&lt;td&gt;Only last one wins&lt;/td&gt;
&lt;td&gt;Only last one wins&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;
ENTRYPOINT locks in the executable; CMD provides the default arguments that a caller can replace without touching the entrypoint.





&lt;h3&gt;
  
  
  EXPOSE — document the port
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;EXPOSE&lt;/code&gt; documents which port the container listens on. It does &lt;strong&gt;not&lt;/strong&gt; publish the port — it is metadata for humans and tools. You still need &lt;code&gt;docker run -p 3000:3000&lt;/code&gt; to actually map it to the host.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 3000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ENV vs ARG — variables
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;ENV&lt;/code&gt; sets environment variables that persist &lt;em&gt;inside the running container&lt;/em&gt; (your app can read them). &lt;code&gt;ARG&lt;/code&gt; sets build-time variables that exist only &lt;em&gt;during the build&lt;/em&gt; and are gone at runtime.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; NODE_VERSION=20&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; NODE_ENV=production&lt;/span&gt;
&lt;span class="c"&gt;# Pass ARG at build: docker build --build-arg NODE_VERSION=20 .&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;ARG&lt;/th&gt;
&lt;th&gt;ENV&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Available during build&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Available in running container&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Set from CLI&lt;/td&gt;
&lt;td&gt;&lt;code&gt;--build-arg&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;-e&lt;/code&gt; at run / Dockerfile&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Good for&lt;/td&gt;
&lt;td&gt;Versions, build flags&lt;/td&gt;
&lt;td&gt;App config, NODE_ENV, ports&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Visible in image history&lt;/td&gt;
&lt;td&gt;Yes (don't put secrets)&lt;/td&gt;
&lt;td&gt;Yes (don't put secrets)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Layer caching — why instruction order matters
&lt;/h3&gt;

&lt;p&gt;Every instruction creates a cached layer. On rebuild, Docker reuses a layer if nothing it depends on changed. The moment one layer is invalidated, &lt;em&gt;every layer after it&lt;/em&gt; is rebuilt too. The classic Node mistake is copying everything first and &lt;em&gt;then&lt;/em&gt; installing dependencies — every source change forces &lt;code&gt;npm install&lt;/code&gt; to re-run.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# GOOD: deps cached separately from source&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package.json package-lock.json ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm ci
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .          # changes often, but npm ci layer stays cached&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Put rarely-changing things (dependencies) early, frequently-changing things (source code) late.&lt;/p&gt;

&lt;h3&gt;
  
  
  A realistic Node app Dockerfile
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:20-alpine&lt;/span&gt;

&lt;span class="c"&gt;# 1. Set the working directory&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="c"&gt;# 2. Copy only manifests first (cache-friendly)&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package.json package-lock.json ./&lt;/span&gt;

&lt;span class="c"&gt;# 3. Install exact, reproducible deps&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm ci

&lt;span class="c"&gt;# 4. Now copy the rest of the source&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;

&lt;span class="c"&gt;# 5. Build the app (e.g. React / Vite / Next build)&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm run build

&lt;span class="c"&gt;# 6. Runtime config&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; NODE_ENV=production&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 3000&lt;/span&gt;

&lt;span class="c"&gt;# 7. Fixed binary + default arg&lt;/span&gt;
&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["node"]&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["dist/server.js"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Answer &lt;code&gt;CMD vs ENTRYPOINT&lt;/code&gt; crisply: ENTRYPOINT is the fixed executable; CMD supplies default arguments the user can override at &lt;code&gt;docker run&lt;/code&gt;. If both are set, CMD is appended to ENTRYPOINT. Mention that exec form (&lt;code&gt;["node","app.js"]&lt;/code&gt;) is preferred so SIGTERM reaches your process for graceful shutdown. That last detail signals real experience.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; Cache invalidation cascades. If you &lt;code&gt;COPY . .&lt;/code&gt; &lt;em&gt;before&lt;/em&gt; &lt;code&gt;RUN npm ci&lt;/code&gt;, then changing any source file busts the cache for the install layer and everything after — your dependencies reinstall on every build. Order from least-changing to most-changing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Use &lt;code&gt;npm ci&lt;/code&gt; instead of &lt;code&gt;npm install&lt;/code&gt; in Dockerfiles. It installs exactly what's in &lt;code&gt;package-lock.json&lt;/code&gt;, fails if the lockfile is out of sync, and is faster and fully reproducible — exactly what you want for deterministic image builds.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Multi-Stage Builds and Image Optimization
&lt;/h2&gt;

&lt;p&gt;A naive image bundles your entire build toolchain — compilers, dev dependencies, source code — into the thing you ship to production. That is bloated and insecure. Multi-stage builds let you build in one stage and copy &lt;em&gt;only the finished artifact&lt;/em&gt; into a clean, tiny final stage.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why image size matters in interviews:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Security:&lt;/strong&gt; fewer packages = smaller attack surface. No compiler, no shell utilities, no dev deps for an attacker to exploit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Speed:&lt;/strong&gt; smaller images pull and start faster — faster deploys, faster autoscaling, faster CI.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost:&lt;/strong&gt; less registry storage, less bandwidth, less disk on every node. At scale this is real money.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Shipping a single-stage image is like mailing someone a cake along with the entire kitchen — oven, mixer, flour bags and all. Multi-stage builds bake the cake in the kitchen, then ship &lt;em&gt;only the cake&lt;/em&gt; in a small box.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Multi-stage Dockerfile: React app served by nginx
&lt;/h3&gt;

&lt;p&gt;Build the static bundle with Node, then throw away Node entirely and serve the files with a tiny nginx image.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# ---- Stage 1: build ----&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;node:20-alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;build&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package.json package-lock.json ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm ci
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm run build          &lt;span class="c"&gt;# produces /app/dist (or /app/build)&lt;/span&gt;

&lt;span class="c"&gt;# ---- Stage 2: serve ----&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; nginx:1.27-alpine&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=build /app/dist /usr/share/nginx/html&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 80&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["nginx", "-g", "daemon off;"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The final image contains nginx and your static files only — no Node, no &lt;code&gt;node_modules&lt;/code&gt;, no source. Typically tens of MB instead of hundreds.&lt;/p&gt;

&lt;h3&gt;
  
  
  Multi-stage Dockerfile: Go binary on distroless
&lt;/h3&gt;

&lt;p&gt;Go compiles to a single static binary, so the final stage needs almost nothing — perfect for distroless.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# ---- Stage 1: build ----&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;golang:1.22-alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;build&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /src&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; go.mod go.sum ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;go mod download
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nv"&gt;CGO_ENABLED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 go build &lt;span class="nt"&gt;-o&lt;/span&gt; /app/server .

&lt;span class="c"&gt;# ---- Stage 2: minimal runtime ----&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; gcr.io/distroless/static-debian12&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=build /app/server /server&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 8080&lt;/span&gt;
&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["/server"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result is often under 10 MB: just your binary plus minimal certs. No shell, no package manager — nothing for an attacker to pivot into.&lt;/p&gt;

&lt;h3&gt;
  
  
  Optimization techniques
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Small base images:&lt;/strong&gt; prefer &lt;code&gt;-alpine&lt;/code&gt;, &lt;code&gt;-slim&lt;/code&gt;, or distroless over full &lt;code&gt;ubuntu&lt;/code&gt;/&lt;code&gt;debian&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-stage builds:&lt;/strong&gt; ship only the artifact, not the toolchain.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Combine RUN layers:&lt;/strong&gt; chain with &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; and clean up in the same layer so the cleanup actually shrinks the image.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;.dockerignore:&lt;/strong&gt; keep &lt;code&gt;node_modules&lt;/code&gt;, &lt;code&gt;.git&lt;/code&gt;, logs out of the build context — faster builds and no accidental bloat.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Order instructions:&lt;/strong&gt; least-changing first for cache hits.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Remove build deps:&lt;/strong&gt; uninstall compilers in the same &lt;code&gt;RUN&lt;/code&gt;, or just leave them in the discarded build stage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;--no-install-recommends&lt;/code&gt;:&lt;/strong&gt; on Debian/Ubuntu, skip optional suggested packages.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Debian/Ubuntu: clean up in the SAME layer&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update &lt;span class="se"&gt;\
&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="nt"&gt;--no-install-recommends&lt;/span&gt; curl ca-certificates &lt;span class="se"&gt;\
&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /var/lib/apt/lists/&lt;span class="k"&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A typical &lt;code&gt;.dockerignore&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node_modules
.git
dist
build
*.log
.env
Dockerfile
.dockerignore
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Base image sizes
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Base image&lt;/th&gt;
&lt;th&gt;Approx size&lt;/th&gt;
&lt;th&gt;Has shell / pkg manager?&lt;/th&gt;
&lt;th&gt;Best for&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ubuntu&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;~75 MB&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Dev convenience, lots of tooling&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;debian:slim&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;~30 MB&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Balanced general-purpose runtime&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;alpine&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;~5 MB&lt;/td&gt;
&lt;td&gt;Yes (busybox + apk)&lt;/td&gt;
&lt;td&gt;Small images, most languages&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;distroless&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;~2–20 MB&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Most secure production runtime&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Alpine is small but can cause issues with native modules due to musl libc. In those cases a &lt;code&gt;-slim&lt;/code&gt; variant or a proper multi-stage build is a better fit.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Before / after: the real win
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker images
REPOSITORY      TAG          SIZE
myapp           single       1.1GB     &lt;span class="c"&gt;# node + source + node_modules&lt;/span&gt;
myapp           multistage   42MB      &lt;span class="c"&gt;# nginx + static build only&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Distroless images contain your app and its runtime — and literally nothing else. No shell, no &lt;code&gt;apt&lt;/code&gt;, no &lt;code&gt;ls&lt;/code&gt;. That kills a whole class of attacks. The trade-off: you can't &lt;code&gt;docker exec&lt;/code&gt; in to poke around, so debug locally or use the &lt;code&gt;:debug&lt;/code&gt; variant. For compiled languages (Go, Rust) and even Java/Python, distroless is the gold-standard production base.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; "How would you reduce a Docker image's size?" Hit these in order: multi-stage build (biggest win), smaller base image (alpine/slim/distroless), &lt;code&gt;.dockerignore&lt;/code&gt;, combine and clean up RUN layers in the same instruction, and remove dev/build dependencies from the final stage. Bonus: mention the security angle — smaller image = smaller attack surface.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; A team shipped a React app as a single-stage Node image at 1.1 GB. Pulls on every deploy took minutes and autoscaling lagged. Switching to a multi-stage build (Node to build, nginx-alpine to serve) dropped it to ~42 MB — a 25x reduction. Deploys went from minutes to seconds.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Data: Volumes vs Bind Mounts
&lt;/h2&gt;

&lt;p&gt;Containers are &lt;strong&gt;ephemeral&lt;/strong&gt;. The writable layer lives and dies with the container — &lt;code&gt;docker rm&lt;/code&gt; the container and that data is gone forever. That is fine for a stateless frontend, but a disaster for a database. To persist data, or to share files between your host and a container, you mount storage from &lt;em&gt;outside&lt;/em&gt; the container's lifecycle.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; A container's own filesystem is like a hotel room — when you check out, housekeeping wipes everything. A &lt;strong&gt;volume&lt;/strong&gt; is your bank locker: it stays safe no matter how many times you check in and out. A &lt;strong&gt;bind mount&lt;/strong&gt; is like leaving a door open straight into your house — convenient, but the container can now mess with your real stuff.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Visual model:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Named volume&lt;/strong&gt; — container writes to &lt;code&gt;/var/lib/...&lt;/code&gt; → Docker-managed storage at &lt;code&gt;/var/lib/docker/volumes/pgdata&lt;/code&gt;. Portable, survives container removal, best for DB data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bind mount&lt;/strong&gt; — container writes to &lt;code&gt;/app&lt;/code&gt; → a specific host folder you pick (e.g. &lt;code&gt;/home/user/project&lt;/code&gt;). Best for live dev where you want host edits visible instantly.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The three mount types
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Named volume:&lt;/strong&gt; Docker-managed storage living under &lt;code&gt;/var/lib/docker/volumes/&lt;/code&gt;. Docker owns it; you reference it by name. Best for persistent app data like databases.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bind mount:&lt;/strong&gt; maps a specific host path straight into the container. You control the exact location. Best for local development (live code).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;tmpfs:&lt;/strong&gt; stored in host RAM only, never written to disk. Vanishes on stop. Best for secrets or scratch data you never want persisted.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Named volume&lt;/th&gt;
&lt;th&gt;Bind mount&lt;/th&gt;
&lt;th&gt;tmpfs&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Location&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/var/lib/docker/volumes/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Any host path you pick&lt;/td&gt;
&lt;td&gt;Host RAM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Managed by&lt;/td&gt;
&lt;td&gt;Docker&lt;/td&gt;
&lt;td&gt;You&lt;/td&gt;
&lt;td&gt;Docker (memory)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Use case&lt;/td&gt;
&lt;td&gt;DB / persistent data&lt;/td&gt;
&lt;td&gt;Live dev, config files&lt;/td&gt;
&lt;td&gt;Secrets, scratch&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Portability&lt;/td&gt;
&lt;td&gt;High (Docker-native)&lt;/td&gt;
&lt;td&gt;Low (host-path dependent)&lt;/td&gt;
&lt;td&gt;N/A (ephemeral)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Performance on Mac/Win&lt;/td&gt;
&lt;td&gt;Fast (in VM)&lt;/td&gt;
&lt;td&gt;Slower (host↔VM sync)&lt;/td&gt;
&lt;td&gt;Fast (RAM)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Survives container rm&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes (it's on host)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;
Named volumes are Docker-managed and portable (best for production data); bind mounts expose a specific host directory (best for local development).





&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; On Mac/Windows, Docker runs inside a Linux VM, so bind mounts involve syncing between host and VM on every file change — hence the slowdown. Named volumes live entirely inside the VM and are therefore fast.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Managing named volumes
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker volume create pgdata          &lt;span class="c"&gt;# create&lt;/span&gt;
docker volume &lt;span class="nb"&gt;ls&lt;/span&gt;                     &lt;span class="c"&gt;# list&lt;/span&gt;
docker volume inspect pgdata         &lt;span class="c"&gt;# see mountpoint, driver&lt;/span&gt;
docker volume &lt;span class="nb"&gt;rm &lt;/span&gt;pgdata              &lt;span class="c"&gt;# delete&lt;/span&gt;
docker volume prune                  &lt;span class="c"&gt;# remove all unused volumes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Two mount syntaxes: -v vs --mount
&lt;/h3&gt;

&lt;p&gt;The short &lt;code&gt;-v&lt;/code&gt; form is compact; the verbose &lt;code&gt;--mount&lt;/code&gt; form is explicit and self-documenting (preferred in scripts).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# -v form: source:target[:options]&lt;/span&gt;
docker run &lt;span class="nt"&gt;-v&lt;/span&gt; pgdata:/var/lib/postgresql/data postgres:16

&lt;span class="c"&gt;# --mount form: explicit key=value&lt;/span&gt;
docker run &lt;span class="nt"&gt;--mount&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;volume,source&lt;span class="o"&gt;=&lt;/span&gt;pgdata,target&lt;span class="o"&gt;=&lt;/span&gt;/var/lib/postgresql/data postgres:16
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Bind mount for live development (hot reload)
&lt;/h3&gt;

&lt;p&gt;Mount your source code into the container so changes on your host instantly reflect inside — the dev server hot-reloads as you edit.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-p&lt;/span&gt; 5173:5173 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;:/app &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; /app/node_modules &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-w&lt;/span&gt; /app &lt;span class="se"&gt;\&lt;/span&gt;
  node:20-alpine npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second &lt;code&gt;-v /app/node_modules&lt;/code&gt; is an anonymous volume that &lt;em&gt;shields&lt;/em&gt; the container's installed &lt;code&gt;node_modules&lt;/code&gt; from being clobbered by the host bind mount — a very common gotcha.&lt;/p&gt;

&lt;h3&gt;
  
  
  Named volume for Postgres data
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; pg &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;secret &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; pgdata:/var/lib/postgresql/data &lt;span class="se"&gt;\&lt;/span&gt;
  postgres:16
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;docker rm -f pg&lt;/code&gt; destroys the container but &lt;code&gt;pgdata&lt;/code&gt; survives. Start a new Postgres container against the same volume and all your tables are still there.&lt;/p&gt;

&lt;h3&gt;
  
  
  When to use which
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Named volume&lt;/strong&gt; → production data that must persist: databases, uploads, caches.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bind mount&lt;/strong&gt; → local dev where you want host edits live in the container, or injecting a config file.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;tmpfs&lt;/strong&gt; → sensitive or throwaway data that should never touch disk.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; A bind mount &lt;em&gt;overlays&lt;/em&gt; the target directory — it hides whatever the image had there. If you bind-mount your source over &lt;code&gt;/app&lt;/code&gt; and the image baked &lt;code&gt;node_modules&lt;/code&gt; into &lt;code&gt;/app/node_modules&lt;/code&gt;, your mount hides them and the app breaks (hence the anonymous-volume trick above). Also watch file permissions: a process running as a non-root UID inside the container may not be able to write to a host directory owned by a different user.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; "Where does a named volume's data actually live?" Answer: on the Docker host under &lt;code&gt;/var/lib/docker/volumes/&amp;lt;name&amp;gt;/_data&lt;/code&gt;, managed by Docker — not inside the container. On Mac/Windows it's inside the Docker Desktop Linux VM, which is why you can't &lt;code&gt;cd&lt;/code&gt; to it from your Mac terminal. Knowing this distinction (host vs container vs VM) shows you actually understand the storage model.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Docker Networking
&lt;/h2&gt;

&lt;p&gt;Every container needs to talk to something — the host, the internet, or another container. Docker handles this through &lt;strong&gt;network drivers&lt;/strong&gt;. When you install Docker on Ubuntu 24.04, three networks already exist out of the box: &lt;code&gt;bridge&lt;/code&gt;, &lt;code&gt;host&lt;/code&gt;, and &lt;code&gt;none&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Think of a Docker network like the wiring in an apartment building. &lt;code&gt;bridge&lt;/code&gt; is the building's internal intercom — flats can call each other through a switchboard, and the building has one main door (the host) to the outside world. &lt;code&gt;host&lt;/code&gt; means the flat has no walls at all and shares the building's street address directly. &lt;code&gt;none&lt;/code&gt; is a sealed room — no phone, no door, total isolation.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Container-to-container on a user-defined bridge:&lt;/strong&gt; On a user-defined network, Docker's embedded DNS lets containers reach each other by name. Only the API container needs a published port (&lt;code&gt;-p 3000:3000&lt;/code&gt;) so the browser can reach it. The &lt;code&gt;db&lt;/code&gt; and &lt;code&gt;cache&lt;/code&gt; containers are never published to the host — secure by default.&lt;/p&gt;

&lt;h3&gt;
  
  
  The three default networks
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Driver&lt;/th&gt;
&lt;th&gt;Isolation&lt;/th&gt;
&lt;th&gt;Use Case&lt;/th&gt;
&lt;th&gt;Performance&lt;/th&gt;
&lt;th&gt;Port Mapping&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;bridge&lt;/strong&gt; (default)&lt;/td&gt;
&lt;td&gt;Containers get private IPs on a virtual subnet; isolated from host network namespace.&lt;/td&gt;
&lt;td&gt;The normal default for single-host apps.&lt;/td&gt;
&lt;td&gt;Slight overhead from NAT + virtual bridge, negligible for most workloads.&lt;/td&gt;
&lt;td&gt;Required. You must &lt;code&gt;-p&lt;/code&gt; to reach a container from outside.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;host&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;None. Container shares the host's network namespace directly.&lt;/td&gt;
&lt;td&gt;Max network performance, or when you need the container on the host's real IP.&lt;/td&gt;
&lt;td&gt;Fastest — no NAT, no bridge, no virtual layer.&lt;/td&gt;
&lt;td&gt;Ignored. &lt;code&gt;-p&lt;/code&gt; does nothing; the container binds host ports directly.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;none&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Total. Only a loopback interface; no external connectivity.&lt;/td&gt;
&lt;td&gt;Security-sensitive batch jobs, or you wire up custom networking yourself.&lt;/td&gt;
&lt;td&gt;N/A — no traffic flows.&lt;/td&gt;
&lt;td&gt;N/A — nothing to publish.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Port mapping deep dive
&lt;/h3&gt;

&lt;p&gt;A container on a bridge network lives on a private subnet (commonly &lt;code&gt;172.17.0.0/16&lt;/code&gt;). The flag &lt;code&gt;-p hostPort:containerPort&lt;/code&gt; tells Docker to take traffic arriving on that host port and forward it (via iptables DNAT rules) into the container's port. Read it as &lt;strong&gt;outside:inside&lt;/strong&gt; — the left number is what you type in the browser; the right number is what the app listens on inside the container.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Map host port 8080 -&amp;gt; container port 80&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:80 &lt;span class="nt"&gt;--name&lt;/span&gt; web nginx

&lt;span class="c"&gt;# Now the host can reach the container:&lt;/span&gt;
curl http://localhost:8080

&lt;span class="c"&gt;# Map only on loopback (NOT exposed to the LAN):&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 127.0.0.1:8080:80 &lt;span class="nt"&gt;--name&lt;/span&gt; web-local nginx

&lt;span class="c"&gt;# Let Docker pick a random free host port:&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 80 &lt;span class="nt"&gt;--name&lt;/span&gt; web-rand nginx
docker port web-rand   &lt;span class="c"&gt;# shows which host port was assigned&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Container-to-container communication — the key part
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Interview scenario:&lt;/strong&gt; "Why can't my two containers talk by name?"&lt;/p&gt;

&lt;p&gt;On the &lt;strong&gt;default&lt;/strong&gt; bridge network, containers can reach each other by IP address, but there is &lt;strong&gt;no automatic DNS&lt;/strong&gt; — you cannot ping or curl another container by its name. On a &lt;strong&gt;user-defined&lt;/strong&gt; bridge network, Docker runs an embedded DNS server and containers resolve each other &lt;strong&gt;by container name automatically&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This is the root cause of 90% of "my app can't connect to my db" bugs.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Almost never use the default bridge for multi-container apps. Always &lt;code&gt;docker network create&lt;/code&gt; a user-defined bridge (or let Docker Compose make one for you — it does this automatically). You get free DNS by container name, better isolation, and you can attach/detach containers on the fly.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Create a user-defined bridge network&lt;/span&gt;
docker network create appnet

&lt;span class="c"&gt;# List networks (you'll see appnet alongside bridge/host/none)&lt;/span&gt;
docker network &lt;span class="nb"&gt;ls&lt;/span&gt;

&lt;span class="c"&gt;# Inspect it: subnet, gateway, connected containers&lt;/span&gt;
docker network inspect appnet

&lt;span class="c"&gt;# 2. Run a db container ON that network&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; db &lt;span class="nt"&gt;--network&lt;/span&gt; appnet &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;secret postgres:16

&lt;span class="c"&gt;# 3. Run a client container on the SAME network&lt;/span&gt;
docker run &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; client &lt;span class="nt"&gt;--network&lt;/span&gt; appnet alpine sh

&lt;span class="c"&gt;# Inside the client shell — resolve 'db' by NAME, no IP needed:&lt;/span&gt;
&lt;span class="c"&gt;#   apk add --no-cache curl&lt;/span&gt;
&lt;span class="c"&gt;#   ping db&lt;/span&gt;
&lt;span class="c"&gt;#   nc -zv db 5432          # TCP connect to postgres by name&lt;/span&gt;

&lt;span class="c"&gt;# Attach an already-running container to a network later:&lt;/span&gt;
docker network connect appnet web
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; "bridge vs host?" — bridge isolates the container on its own subnet and needs &lt;code&gt;-p&lt;/code&gt; port publishing + NAT to be reached; host removes that isolation, the container uses the host's network stack directly (no &lt;code&gt;-p&lt;/code&gt;, fastest, but port conflicts and less isolation). "Why can't my two containers talk by name on the default bridge?" — because the default bridge has no embedded DNS; name resolution only works on user-defined networks. Fix: create a user-defined bridge and run both containers on it (or use Compose).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; When you write &lt;code&gt;-p 8080:80&lt;/code&gt;, Docker binds &lt;code&gt;0.0.0.0:8080&lt;/code&gt; by default — meaning the port is exposed on &lt;em&gt;every&lt;/em&gt; network interface, including your public/LAN IP, not just localhost. On a cloud VM with a public IP, that database or admin panel is now reachable from the internet. Bind to &lt;code&gt;127.0.0.1&lt;/code&gt; (e.g. &lt;code&gt;-p 127.0.0.1:8080:80&lt;/code&gt;) for local-only access. Docker's iptables rules can also bypass a UFW firewall — don't assume UFW alone protects published ports.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Docker Compose
&lt;/h2&gt;

&lt;p&gt;Running one container with a long &lt;code&gt;docker run&lt;/code&gt; command is fine. But a real app is a &lt;em&gt;web frontend + an API + a database + a cache&lt;/em&gt;, each needing its own ports, volumes, env vars, and a shared network. Typing four &lt;code&gt;docker run&lt;/code&gt; commands in the right order, every time, is painful and error-prone. &lt;strong&gt;Docker Compose&lt;/strong&gt; solves this: you declare your entire multi-container stack in one YAML file and bring it all up with a single command. Use this with the &lt;a href="https://opscanopy.com/docker-run-to-compose/" rel="noopener noreferrer"&gt;Docker run → Compose converter&lt;/a&gt; to translate existing &lt;code&gt;docker run&lt;/code&gt; commands into Compose services quickly.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If &lt;code&gt;docker run&lt;/code&gt; is cooking one dish, Compose is the full recipe card for a thali — it lists every dish (service), the ingredients (image, env, volumes), and the order to serve them (depends_on). One command and the whole meal arrives, plated the same way every single time.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Compose stack layout:&lt;/strong&gt; A single &lt;code&gt;docker compose up&lt;/code&gt; creates the network, the volume, and all three services. The &lt;code&gt;api&lt;/code&gt; service builds from a local Dockerfile; &lt;code&gt;db&lt;/code&gt; uses a named volume for persistence; all three are wired by service name via Compose's built-in DNS.&lt;/p&gt;

&lt;h3&gt;
  
  
  "docker compose" vs "docker-compose"
&lt;/h3&gt;

&lt;p&gt;Old Compose was a separate Python tool invoked as &lt;code&gt;docker-compose&lt;/code&gt; (with a hyphen). Modern Compose is a &lt;strong&gt;v2 plugin&lt;/strong&gt; built into the Docker CLI, invoked as &lt;code&gt;docker compose&lt;/code&gt; (a space, no hyphen). On Ubuntu 24.04 with current Docker, use &lt;strong&gt;&lt;code&gt;docker compose&lt;/code&gt;&lt;/strong&gt;. The hyphenated command is legacy and deprecated. Also note: the top-level &lt;code&gt;version:&lt;/code&gt; key is now obsolete and can be omitted.&lt;/p&gt;

&lt;h3&gt;
  
  
  Anatomy of a Compose file
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Key&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;image&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pull a prebuilt image (e.g. &lt;code&gt;postgres:16&lt;/code&gt;).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;build&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Build from a local Dockerfile instead of pulling.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ports&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Publish ports to the host — &lt;code&gt;"host:container"&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;volumes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Mount named volumes or host paths for persistent data.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;environment&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Set env vars inside the container.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;depends_on&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Control start order (and optionally wait for health).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;networks&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Attach the service to one or more networks.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;healthcheck&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Define how Docker tests if the service is actually ready.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  A full, realistic compose.yaml
&lt;/h3&gt;

&lt;p&gt;A Node API built from a local Dockerfile, a Postgres database with a named volume, and a Redis cache — all on a shared network. The API waits for Postgres to be &lt;em&gt;healthy&lt;/em&gt;, not just started.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;api&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./api&lt;/span&gt;                 &lt;span class="c1"&gt;# builds from ./api/Dockerfile&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3000:3000"&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;DB_HOST&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;db&lt;/span&gt;                &lt;span class="c1"&gt;# resolves by service name (Compose DNS)&lt;/span&gt;
      &lt;span class="na"&gt;DB_PORT&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5432&lt;/span&gt;
      &lt;span class="na"&gt;DB_USER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;appuser&lt;/span&gt;
      &lt;span class="na"&gt;DB_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${DB_PASSWORD}&lt;/span&gt;    &lt;span class="c1"&gt;# pulled from .env file&lt;/span&gt;
      &lt;span class="na"&gt;REDIS_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis://cache:6379&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;db&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;service_healthy&lt;/span&gt;   &lt;span class="c1"&gt;# wait until db healthcheck passes&lt;/span&gt;
      &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;service_started&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;appnet&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;

  &lt;span class="na"&gt;db&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres:16&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_USER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;appuser&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${DB_PASSWORD}&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_DB&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;appdb&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;pgdata:/var/lib/postgresql/data&lt;/span&gt;   &lt;span class="c1"&gt;# named volume = data survives restarts&lt;/span&gt;
    &lt;span class="na"&gt;healthcheck&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CMD-SHELL"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pg_isready&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-U&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;appuser&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-d&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;appdb"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10s&lt;/span&gt;
      &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;5s&lt;/span&gt;
      &lt;span class="na"&gt;retries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
      &lt;span class="na"&gt;start_period&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10s&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;appnet&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;

  &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis:7-alpine&lt;/span&gt;
    &lt;span class="na"&gt;healthcheck&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CMD"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;redis-cli"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ping"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10s&lt;/span&gt;
      &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;3s&lt;/span&gt;
      &lt;span class="na"&gt;retries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;appnet&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;

&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pgdata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;                        &lt;span class="c1"&gt;# Docker-managed named volume&lt;/span&gt;

&lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;appnet&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;                        &lt;span class="c1"&gt;# user-defined bridge -&amp;gt; DNS by service name&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because every service is on &lt;code&gt;appnet&lt;/code&gt;, the API reaches the database at hostname &lt;code&gt;db&lt;/code&gt; and Redis at &lt;code&gt;cache&lt;/code&gt; — the service names &lt;em&gt;are&lt;/em&gt; the hostnames, thanks to Compose's user-defined network (exactly the DNS behaviour covered in the networking section).&lt;/p&gt;

&lt;h3&gt;
  
  
  Key commands
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt;          &lt;span class="c"&gt;# build (if needed) + start all, detached&lt;/span&gt;
docker compose ps             &lt;span class="c"&gt;# list services in this project + their state&lt;/span&gt;
docker compose logs &lt;span class="nt"&gt;-f&lt;/span&gt;        &lt;span class="c"&gt;# tail combined logs from all services&lt;/span&gt;
docker compose logs &lt;span class="nt"&gt;-f&lt;/span&gt; api    &lt;span class="c"&gt;# tail just one service&lt;/span&gt;
docker compose build          &lt;span class="c"&gt;# (re)build images that use 'build:'&lt;/span&gt;
docker compose &lt;span class="nb"&gt;exec &lt;/span&gt;api sh    &lt;span class="c"&gt;# open a shell inside the running api container&lt;/span&gt;
docker compose down           &lt;span class="c"&gt;# stop + remove containers and the network&lt;/span&gt;
docker compose down &lt;span class="nt"&gt;-v&lt;/span&gt;        &lt;span class="c"&gt;# ALSO delete named volumes (wipes db data!)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  depends_on vs healthcheck — the trap
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Interview scenario:&lt;/strong&gt; "Does &lt;code&gt;depends_on&lt;/code&gt; wait for a service to be ready?"&lt;/p&gt;

&lt;p&gt;A plain &lt;code&gt;depends_on: [db]&lt;/code&gt; only controls &lt;strong&gt;start order&lt;/strong&gt;. Compose will start &lt;code&gt;db&lt;/code&gt; &lt;em&gt;before&lt;/em&gt; &lt;code&gt;api&lt;/code&gt; — but "started" just means the container process launched, &lt;strong&gt;not&lt;/strong&gt; that Postgres is accepting connections. Databases take a few seconds to initialise, so your API can crash on boot trying to connect to a not-yet-ready DB.&lt;/p&gt;

&lt;p&gt;For readiness, add a &lt;code&gt;healthcheck&lt;/code&gt; to the dependency service and use &lt;code&gt;condition: service_healthy&lt;/code&gt; in the dependent service — only then does Compose wait.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Keep secrets and environment-specific values out of the compose file. Put them in a &lt;code&gt;.env&lt;/code&gt; file (auto-loaded by Compose for &lt;code&gt;${VAR}&lt;/code&gt; substitution) and add &lt;code&gt;.env&lt;/code&gt; to &lt;code&gt;.gitignore&lt;/code&gt;. For per-environment tweaks, use a &lt;code&gt;compose.override.yaml&lt;/code&gt; (auto-merged in dev) or &lt;code&gt;docker compose -f compose.yaml -f compose.prod.yaml up&lt;/code&gt; to layer a production config on top. Use the &lt;a href="https://opscanopy.com/env-example-checker/" rel="noopener noreferrer"&gt;.env Example Checker&lt;/a&gt; to validate that your &lt;code&gt;.env.example&lt;/code&gt; stays in sync with your actual &lt;code&gt;.env&lt;/code&gt; keys.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; A new dev joining a team, instead of working through a half-day setup doc, can clone the repo, copy &lt;code&gt;.env.example&lt;/code&gt; to &lt;code&gt;.env&lt;/code&gt;, and run &lt;code&gt;docker compose up -d&lt;/code&gt;. Sixty seconds later the API, database, and cache are running and wired together identically to every other machine — including CI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Expect: "What does Compose actually do for you?" — declarative multi-container orchestration on a &lt;em&gt;single host&lt;/em&gt;: networks, volumes, env, and start order in one file. Be ready for "Does depends_on wait for the service to be ready?" — No, only start order; for readiness add a healthcheck + &lt;code&gt;condition: service_healthy&lt;/code&gt;. Note the scope: Compose is single-host; for multi-host/cluster orchestration you move to Kubernetes — see &lt;a href="https://opscanopy.com/learn/guides/kubernetes-for-devops/" rel="noopener noreferrer"&gt;Kubernetes for DevOps&lt;/a&gt; for a full introduction. See also the &lt;a href="https://opscanopy.com/kubernetes-resource-calculator/" rel="noopener noreferrer"&gt;Kubernetes Resource Calculator&lt;/a&gt; when you are ready to plan resource requests for a containerised workload running in a cluster. Mentioning this boundary shows you understand where Compose stops.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; &lt;code&gt;docker compose down -v&lt;/code&gt; deletes named volumes — that &lt;code&gt;pgdata&lt;/code&gt; volume holding your database goes with it. Great for a clean dev reset, catastrophic on anything you care about. Plain &lt;code&gt;docker compose down&lt;/code&gt; (no &lt;code&gt;-v&lt;/code&gt;) keeps volumes. Build the muscle memory now, before you run it on the wrong project.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Best Practices and Security
&lt;/h2&gt;

&lt;p&gt;A container that &lt;em&gt;works&lt;/em&gt; and a container that is &lt;em&gt;production-ready&lt;/em&gt; are different things. This section is the checklist that separates "it runs on my machine" from "it's safe to ship." Most of it shows up in DevOps interviews too — security questions are nearly guaranteed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Run as non-root
&lt;/h3&gt;

&lt;p&gt;By default, processes in a container run as &lt;strong&gt;root&lt;/strong&gt; (UID 0). If an attacker breaks out of the app, they are root inside the container — and a container root maps to host root in several breakout scenarios. The fix is the &lt;code&gt;USER&lt;/code&gt; instruction: create an unprivileged user and switch to it before the app runs. This is the single highest-impact security change you can make.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:20-slim&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="c"&gt;# Install deps as root (needs write to /app)&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm ci &lt;span class="nt"&gt;--omit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;dev

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;

&lt;span class="c"&gt;# Create a non-root user and hand over ownership&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;useradd &lt;span class="nt"&gt;--system&lt;/span&gt; &lt;span class="nt"&gt;--uid&lt;/span&gt; 10001 &lt;span class="nt"&gt;--no-create-home&lt;/span&gt; appuser &lt;span class="se"&gt;\
&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; appuser:appuser /app

&lt;span class="c"&gt;# Everything from here runs as appuser, not root&lt;/span&gt;
&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; appuser&lt;/span&gt;

&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 3000&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["node", "server.js"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  .dockerignore
&lt;/h3&gt;

&lt;p&gt;Just like &lt;code&gt;.gitignore&lt;/code&gt;, a &lt;code&gt;.dockerignore&lt;/code&gt; file tells the build to skip files when sending the build context to the daemon. This makes builds faster, images smaller, and — critically — stops secrets and junk from being copied in by a broad &lt;code&gt;COPY . .&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# .dockerignore
node_modules
npm-debug.log
.git
.gitignore
.env
.env.*
*.md
Dockerfile
.dockerignore
dist
coverage
.vscode
.DS_Store
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Excluding &lt;code&gt;node_modules&lt;/code&gt; forces a clean &lt;code&gt;npm ci&lt;/code&gt; inside the image (no host-OS native modules leaking in), and excluding &lt;code&gt;.env&lt;/code&gt; / &lt;code&gt;.git&lt;/code&gt; prevents secrets and history from ever entering the image.&lt;/p&gt;

&lt;h3&gt;
  
  
  Don't bake secrets into images
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Interview scenario:&lt;/strong&gt; "How do you handle secrets in Docker?"&lt;/p&gt;

&lt;p&gt;Anything you put in an &lt;code&gt;ENV&lt;/code&gt; instruction or pass as a build &lt;code&gt;ARG&lt;/code&gt; is baked into an image layer — and image layers are &lt;strong&gt;readable by anyone with the image&lt;/strong&gt;. &lt;code&gt;docker history&lt;/code&gt; dumps them right out.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# An attacker (or teammate) with your image can read baked-in values:&lt;/span&gt;
docker &lt;span class="nb"&gt;history&lt;/span&gt; &lt;span class="nt"&gt;--no-trunc&lt;/span&gt; your-image:tag

&lt;span class="c"&gt;# WRONG — secret is now permanently in a layer:&lt;/span&gt;
&lt;span class="c"&gt;#   ENV API_KEY=sk_live_abcd1234&lt;/span&gt;
&lt;span class="c"&gt;#   ARG DB_PASSWORD   (visible in history even if "removed" later)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead, inject secrets at &lt;strong&gt;runtime&lt;/strong&gt;, never at build time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Runtime env (not in the image, but visible via 'docker inspect'):&lt;/span&gt;
docker run &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; myapp

&lt;span class="c"&gt;# Better: file-based secrets (Compose/Swarm) mounted at /run/secrets/...&lt;/span&gt;
&lt;span class="c"&gt;# Best: an external secrets manager (Vault, AWS Secrets Manager,&lt;/span&gt;
&lt;span class="c"&gt;#       SSM Parameter Store) fetched by the app at startup.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Image scanning
&lt;/h3&gt;

&lt;p&gt;Your base image and dependencies carry known vulnerabilities (CVEs). Scan before you ship, and re-scan regularly — new CVEs are disclosed against old images all the time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Docker Scout (built into modern Docker Desktop / CLI)&lt;/span&gt;
docker scout cves your-image:tag
docker scout quickview your-image:tag

&lt;span class="c"&gt;# Trivy (popular open-source scanner) on Ubuntu 24.04&lt;/span&gt;
trivy image your-image:tag
trivy image &lt;span class="nt"&gt;--severity&lt;/span&gt; HIGH,CRITICAL your-image:tag
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Hardening checklist
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Do&lt;/th&gt;
&lt;th&gt;Don't&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Pin base images by version &lt;em&gt;and&lt;/em&gt; digest: &lt;code&gt;node:20-slim@sha256:…&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Use &lt;code&gt;:latest&lt;/code&gt; — it silently changes under you.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Use minimal images (&lt;code&gt;-slim&lt;/code&gt;, &lt;code&gt;alpine&lt;/code&gt;, &lt;code&gt;distroless&lt;/code&gt;) — smaller attack surface.&lt;/td&gt;
&lt;td&gt;Ship a full &lt;code&gt;ubuntu&lt;/code&gt; base with compilers and shells you don't need.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Run as a non-root &lt;code&gt;USER&lt;/code&gt;.&lt;/td&gt;
&lt;td&gt;Run the app as root.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Drop Linux capabilities: &lt;code&gt;--cap-drop ALL&lt;/code&gt;, add back only what's needed.&lt;/td&gt;
&lt;td&gt;Run with default (or &lt;code&gt;--privileged&lt;/code&gt;) capabilities.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Run read-only where possible: &lt;code&gt;--read-only&lt;/code&gt; + &lt;code&gt;--tmpfs /tmp&lt;/code&gt;.&lt;/td&gt;
&lt;td&gt;Leave the whole filesystem writable.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Add a &lt;code&gt;HEALTHCHECK&lt;/code&gt; so orchestrators know real readiness.&lt;/td&gt;
&lt;td&gt;Assume "process running" means "service healthy."&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Inject secrets at runtime via env files / secrets managers.&lt;/td&gt;
&lt;td&gt;Bake secrets into &lt;code&gt;ENV&lt;/code&gt;/&lt;code&gt;ARG&lt;/code&gt; layers.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Use multi-stage builds to keep build tools out of the final image.&lt;/td&gt;
&lt;td&gt;Ship the entire build toolchain in production.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Least-privilege runtime: drop all caps, read-only root fs, writable /tmp&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--cap-drop&lt;/span&gt; ALL &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--read-only&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--tmpfs&lt;/span&gt; /tmp &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; myapp myapp:1.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;HEALTHCHECK&lt;/span&gt;&lt;span class="s"&gt; --interval=30s --timeout=3s --start-period=10s --retries=3 \&lt;/span&gt;
  CMD curl -f http://localhost:3000/health || exit 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; Secrets in image layers are &lt;strong&gt;permanent and readable by anyone with the image&lt;/strong&gt;. Deleting the &lt;code&gt;ENV&lt;/code&gt; line in a later layer does NOT remove it — the earlier layer still contains it, and &lt;code&gt;docker history --no-trunc&lt;/code&gt; reveals it. If you ever accidentally bake a real credential into an image and push it, treat that credential as &lt;em&gt;compromised&lt;/em&gt;: rotate it immediately. You can't "edit it out" of a published image.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Container security questions cluster around a few themes: (1) run as non-root (&lt;code&gt;USER&lt;/code&gt;); (2) minimal/pinned base images to shrink attack surface; (3) never bake secrets into layers (explain &lt;code&gt;docker history&lt;/code&gt;); (4) scan images for CVEs (Scout/Trivy) in CI; (5) least privilege at runtime — drop capabilities, read-only fs, no &lt;code&gt;--privileged&lt;/code&gt;. Bonus: the container shares the host kernel, so a kernel exploit can escape — keep the host patched and consider extra isolation (gVisor, rootless Docker).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Bake scanning into CI as a gate: fail the pipeline on &lt;code&gt;HIGH&lt;/code&gt;/&lt;code&gt;CRITICAL&lt;/code&gt; CVEs (&lt;code&gt;trivy image --exit-code 1 --severity HIGH,CRITICAL&lt;/code&gt;). Combined with pinned base digests, this means an image can't silently drift into a vulnerable state between builds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; A fintech team shipped a Node image with &lt;code&gt;ENV STRIPE_KEY=sk_live_…&lt;/code&gt; baked in "temporarily." The image was pushed to a registry a contractor could pull. Months later, during an audit, &lt;code&gt;docker history&lt;/code&gt; surfaced the live key sitting in a layer. They had to rotate the Stripe key, invalidate the image across every environment, and add a CI secret-scanner — a week of cleanup that one runtime &lt;code&gt;-e&lt;/code&gt; flag (or a secrets manager) would have prevented entirely.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Troubleshooting Playbook
&lt;/h2&gt;

&lt;p&gt;Think of this like a diagnostic chart: a container presents a &lt;strong&gt;symptom&lt;/strong&gt;, you run a &lt;strong&gt;diagnose command&lt;/strong&gt; to confirm the &lt;strong&gt;cause&lt;/strong&gt;, then you apply the &lt;strong&gt;fix&lt;/strong&gt;. You see a blank screen (symptom), open DevTools console (diagnose), spot the error (cause), patch the code (fix). Containers are no different — the tools just have different names.&lt;/p&gt;

&lt;p&gt;90% of Docker problems are solved by three commands run in order: see what state the container is in (&lt;code&gt;docker ps -a&lt;/code&gt;), read what it logged before dying (&lt;code&gt;docker logs&lt;/code&gt;), then either inspect its config or jump inside it (&lt;code&gt;docker inspect&lt;/code&gt; / &lt;code&gt;docker exec -it&lt;/code&gt;). Master this loop and you will look like a wizard in interviews.&lt;/p&gt;

&lt;h3&gt;
  
  
  Symptom → Diagnose → Fix chart
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Symptom&lt;/th&gt;
&lt;th&gt;Diagnose (command)&lt;/th&gt;
&lt;th&gt;Likely Cause&lt;/th&gt;
&lt;th&gt;Fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Container won't start / stuck in &lt;code&gt;Created&lt;/code&gt; or &lt;code&gt;Restarting&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;docker ps -a&lt;/code&gt; then &lt;code&gt;docker logs &amp;lt;name&amp;gt;&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Bad entrypoint, missing config/env, app crashes on boot, or a restart loop&lt;/td&gt;
&lt;td&gt;Read logs, fix the underlying error, rebuild and re-run&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Container exits immediately (&lt;code&gt;Exited (0)&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;docker ps -a&lt;/code&gt; + &lt;code&gt;docker logs &amp;lt;name&amp;gt;&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;No long-running foreground process; &lt;code&gt;CMD&lt;/code&gt; finished and nothing kept PID 1 alive&lt;/td&gt;
&lt;td&gt;Run a process that stays in the foreground (e.g. &lt;code&gt;node server.js&lt;/code&gt;); don't background your main process&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Exits with non-zero code (&lt;code&gt;Exited (1)&lt;/code&gt;, &lt;code&gt;Exited (127)&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker logs &amp;lt;name&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;App threw on startup; &lt;code&gt;127&lt;/code&gt; usually means "command not found"&lt;/td&gt;
&lt;td&gt;Fix the app error; for &lt;code&gt;127&lt;/code&gt;, verify the binary exists and the &lt;code&gt;CMD&lt;/code&gt; path is correct&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Image is huge (1GB+ for a small app)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;docker images&lt;/code&gt; and &lt;code&gt;docker history &amp;lt;image&amp;gt;&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Full base image, build tools shipped, no &lt;code&gt;.dockerignore&lt;/code&gt;, dev deps baked in&lt;/td&gt;
&lt;td&gt;Multi-stage build, slim/alpine base, add &lt;code&gt;.dockerignore&lt;/code&gt;, combine &lt;code&gt;RUN&lt;/code&gt; layers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;port is already allocated&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;docker ps&lt;/code&gt; then &lt;code&gt;sudo lsof -i :3000&lt;/code&gt; (or `ss -ltnp \&lt;/td&gt;
&lt;td&gt;grep 3000`)&lt;/td&gt;
&lt;td&gt;Another container or host process is bound to that port&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;permission denied&lt;/code&gt; on a bind-mounted folder&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;docker exec -it &amp;lt;name&amp;gt; ls -ln /path&lt;/code&gt; and &lt;code&gt;id&lt;/code&gt; inside&lt;/td&gt;
&lt;td&gt;UID/GID inside container ≠ host file owner; or SELinux/AppArmor&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;--user $(id -u):$(id -g)&lt;/code&gt;, &lt;code&gt;chown&lt;/code&gt; the host dir, or add &lt;code&gt;:z&lt;/code&gt;/&lt;code&gt;:Z&lt;/code&gt; on SELinux&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Container A can't reach B by name&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;docker network inspect bridge&lt;/code&gt;; &lt;code&gt;docker exec -it A ping B&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Both on the &lt;strong&gt;default&lt;/strong&gt; &lt;code&gt;bridge&lt;/code&gt;, which has no DNS by name&lt;/td&gt;
&lt;td&gt;Create a user-defined bridge and attach both: &lt;code&gt;docker network create appnet&lt;/code&gt; + &lt;code&gt;--network appnet&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;no space left on device&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;docker system df&lt;/code&gt; and &lt;code&gt;df -h&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Dangling images, stopped containers, unused volumes, build cache eating disk&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;docker system prune -af&lt;/code&gt; (add &lt;code&gt;--volumes&lt;/code&gt; only if sure)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cannot pull (&lt;code&gt;denied&lt;/code&gt;, &lt;code&gt;unauthorized&lt;/code&gt;, &lt;code&gt;toomanyrequests&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Read the exact error from &lt;code&gt;docker pull &amp;lt;image&amp;gt;&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Not logged in, wrong tag, or Docker Hub anonymous rate limit&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;docker login&lt;/code&gt;; fix the tag; for rate limits, authenticate or wait&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Code changes not reflecting after rebuild&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;docker build&lt;/code&gt; output (watch for &lt;code&gt;CACHED&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Cached layer reused; or running an old image/container&lt;/td&gt;
&lt;td&gt;Rebuild with &lt;code&gt;--no-cache&lt;/code&gt;, re-tag and re-run; in dev, bind-mount source&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Env var not set inside the container&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;docker exec -it &amp;lt;name&amp;gt; env&lt;/code&gt; or &lt;code&gt;docker inspect&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;-e&lt;/code&gt;/&lt;code&gt;--env-file&lt;/code&gt; not passed, or it's build-time &lt;code&gt;ARG&lt;/code&gt; not runtime &lt;code&gt;ENV&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Pass &lt;code&gt;-e KEY=value&lt;/code&gt; or &lt;code&gt;--env-file .env&lt;/code&gt;; don't confuse &lt;code&gt;ARG&lt;/code&gt; with &lt;code&gt;ENV&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  The general debugging workflow
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. What is the state? (running, exited, restarting — and the exit code)&lt;/span&gt;
docker ps &lt;span class="nt"&gt;-a&lt;/span&gt;

&lt;span class="c"&gt;# 2. What did it say before it died? (your single most useful command)&lt;/span&gt;
docker logs &amp;lt;container_name_or_id&amp;gt;
docker logs &lt;span class="nt"&gt;-f&lt;/span&gt; &amp;lt;container&amp;gt;          &lt;span class="c"&gt;# follow live, like tail -f&lt;/span&gt;
docker logs &lt;span class="nt"&gt;--tail&lt;/span&gt; 50 &amp;lt;container&amp;gt;   &lt;span class="c"&gt;# last 50 lines only&lt;/span&gt;

&lt;span class="c"&gt;# 3. How is it configured? (env, mounts, network, IP, command)&lt;/span&gt;
docker inspect &amp;lt;container&amp;gt;
docker inspect &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s1"&gt;'{{.State.ExitCode}}'&lt;/span&gt; &amp;lt;container&amp;gt;   &lt;span class="c"&gt;# just the exit code&lt;/span&gt;

&lt;span class="c"&gt;# 4. Get a shell inside a RUNNING container and poke around&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; &amp;lt;container&amp;gt; sh     &lt;span class="c"&gt;# or 'bash' if the image has it&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;/p&gt;
Four-step crash-debug loop: check state, read logs, reproduce interactively, then fix and rebuild.





&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; A classic question is "a container starts and immediately stops, how do you debug it?" The winning answer names the loop: &lt;code&gt;docker ps -a&lt;/code&gt; to confirm it exited and see the code, &lt;code&gt;docker logs&lt;/code&gt; to read the error, then explain the most common cause — there was no long-running foreground process keeping PID 1 alive, or the app crashed on startup. If you can't &lt;code&gt;exec&lt;/code&gt; into it (because it already exited), mention you can override the entrypoint to debug: &lt;code&gt;docker run -it --entrypoint sh &amp;lt;image&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Deep dive: container exits immediately
&lt;/h3&gt;

&lt;p&gt;This is the #1 beginner trap and the #1 interview question. A container lives only as long as its main process (PID 1) runs. The moment that process exits, the container exits. It is like running &lt;code&gt;npm run build&lt;/code&gt; and expecting a dev server to stay up — build finishes, process ends, done. You needed &lt;code&gt;npm start&lt;/code&gt; (a long-running server) instead.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Confirm it exited and check the code&lt;/span&gt;
docker ps &lt;span class="nt"&gt;-a&lt;/span&gt;
&lt;span class="c"&gt;# "Exited (0)" = process finished cleanly, nothing to keep it alive&lt;/span&gt;
&lt;span class="c"&gt;# "Exited (1)" or higher = app crashed; read the logs&lt;/span&gt;

docker logs &amp;lt;container&amp;gt;

&lt;span class="c"&gt;# Can't exec because it already died? Override the entrypoint to get a shell:&lt;/span&gt;
docker run &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;--entrypoint&lt;/span&gt; sh myimage
&lt;span class="c"&gt;# Now run your CMD by hand and watch it fail in real time&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# WRONG — the &amp;amp; backgrounds it, CMD returns instantly, container dies&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; node server.js &amp;amp;&lt;/span&gt;

&lt;span class="c"&gt;# RIGHT — runs in the foreground, holds the container open&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["node", "server.js"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deep dive: port is already allocated
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Find who is holding host port 3000&lt;/span&gt;
docker ps                         &lt;span class="c"&gt;# is another container mapping :3000?&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;lsof &lt;span class="nt"&gt;-i&lt;/span&gt; :3000                &lt;span class="c"&gt;# any host process on it?&lt;/span&gt;
ss &lt;span class="nt"&gt;-ltnp&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; :3000             &lt;span class="c"&gt;# alternative on Ubuntu 24.04&lt;/span&gt;

&lt;span class="c"&gt;# Fix A: stop the offender&lt;/span&gt;
docker stop &amp;lt;other_container&amp;gt;

&lt;span class="c"&gt;# Fix B: just map to a free host port (left = host, right = container)&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 3001:3000 myimage
&lt;span class="c"&gt;# App still listens on 3000 inside; you reach it at http://localhost:3001&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deep dive: image too big
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# See sizes and which layers are fat&lt;/span&gt;
docker images
docker &lt;span class="nb"&gt;history&lt;/span&gt; &amp;lt;image&amp;gt;     &lt;span class="c"&gt;# shows size added per layer&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Multi-stage: build stage has all the tooling, final stage is slim&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;node:20&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;build&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm ci
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm run build

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;node:20-slim&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;runtime&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=build /app/dist ./dist&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm ci &lt;span class="nt"&gt;--omit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;dev
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["node", "dist/server.js"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deep dive: no space left on device
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# See the breakdown: images, containers, volumes, build cache&lt;/span&gt;
docker system &lt;span class="nb"&gt;df
&lt;/span&gt;docker system &lt;span class="nb"&gt;df&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt;        &lt;span class="c"&gt;# verbose, per-item&lt;/span&gt;

&lt;span class="c"&gt;# Reclaim everything unused (stopped containers, dangling/unused images, networks, build cache)&lt;/span&gt;
docker system prune &lt;span class="nt"&gt;-af&lt;/span&gt;    &lt;span class="c"&gt;# -a = also unused images, -f = no prompt&lt;/span&gt;

&lt;span class="c"&gt;# Target just one type if you prefer&lt;/span&gt;
docker image prune &lt;span class="nt"&gt;-af&lt;/span&gt;
docker container prune &lt;span class="nt"&gt;-f&lt;/span&gt;
docker builder prune &lt;span class="nt"&gt;-af&lt;/span&gt;   &lt;span class="c"&gt;# build cache only&lt;/span&gt;

&lt;span class="c"&gt;# Include volumes too — DANGER, can delete data (see warning)&lt;/span&gt;
docker system prune &lt;span class="nt"&gt;-af&lt;/span&gt; &lt;span class="nt"&gt;--volumes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; &lt;code&gt;docker system prune --volumes&lt;/code&gt; and &lt;code&gt;docker volume prune&lt;/code&gt; delete &lt;strong&gt;anonymous and unused volumes&lt;/strong&gt; — and that means real, persistent data (databases, uploads) can be wiped permanently. There is no undo. Never run &lt;code&gt;prune --volumes&lt;/code&gt; on a production host without first confirming which volumes are in use (&lt;code&gt;docker volume ls&lt;/code&gt;). When in doubt, prune images and build cache only.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Run &lt;code&gt;docker system df&lt;/code&gt; &lt;em&gt;before&lt;/em&gt; you blindly prune — it tells you whether the space is in images, volumes, or build cache, so you can reclaim surgically. Also, &lt;code&gt;docker logs&lt;/code&gt; only works if the app writes to &lt;code&gt;stdout&lt;/code&gt;/&lt;code&gt;stderr&lt;/code&gt;; apps that log to a file inside the container will show nothing, so always configure your app to log to the console in containers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Prefer a day-by-day path? This is covered in &lt;a href="https://opscanopy.com/mission-90/" rel="noopener noreferrer"&gt;&lt;strong&gt;Mission 90 Days 22–30&lt;/strong&gt;&lt;/a&gt; — a free 90-day guided DevOps program with browser terminal missions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; A team's CI runner kept failing builds with &lt;code&gt;no space left on device&lt;/code&gt; every Friday. &lt;code&gt;docker system df&lt;/code&gt; revealed 40 GB of build cache. They added &lt;code&gt;docker builder prune -af --filter "until=168h"&lt;/code&gt; (clear cache older than 7 days) to a nightly cron job. Builds stopped failing, and they never touched volumes, so no data was at risk. Diagnose with &lt;code&gt;system df&lt;/code&gt; first, then prune the specific thing that grew.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>docker</category>
      <category>interview</category>
      <category>devops</category>
      <category>containers</category>
    </item>
    <item>
      <title>Docker for DevOps: A Deep Dive into Images, Layers, and Compose</title>
      <dc:creator>opscanopy</dc:creator>
      <pubDate>Sun, 16 Aug 2026 15:30:40 +0000</pubDate>
      <link>https://dev.to/opscanopy/docker-for-devops-a-deep-dive-into-images-layers-and-compose-1a6c</link>
      <guid>https://dev.to/opscanopy/docker-for-devops-a-deep-dive-into-images-layers-and-compose-1a6c</guid>
      <description>&lt;p&gt;Docker is the tool that finally made "it works on my machine" a non-excuse. By packaging an application together with its exact dependencies into a portable image, Docker lets the same artifact run identically on a laptop, a CI runner, and a production server. This guide is a deep, practical tour for DevOps engineers: how images and layers actually work, how to write efficient and secure Dockerfiles, how containers talk to each other over networks, how to persist data with volumes, how to compose multi-service stacks, and how to take all of it to production. Work through it top to bottom, or jump to the part you need. For a structured learning path beyond this guide, see the &lt;a href="https://opscanopy.com/learn/roadmaps/docker/" rel="noopener noreferrer"&gt;Docker roadmap&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem Docker Solves
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The "Works on My Machine" Story
&lt;/h3&gt;

&lt;p&gt;It's 2019. A developer at a startup has been grinding for &lt;strong&gt;3 weeks&lt;/strong&gt; on a Node.js app — a real-time dashboard for tracking delivery agents. The UI is slick, the APIs are fast, the PM is happy. He types &lt;code&gt;npm start&lt;/code&gt; and everything just works. Perfectly.&lt;/p&gt;

&lt;p&gt;He pushes the code to staging. The CI pipeline goes green. He Slacks his team: &lt;em&gt;"Deployed! Please test."&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;3 minutes later:&lt;/strong&gt;&lt;/p&gt;


&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error: Cannot find module 'optional-chaining-polyfill'
Error: node: /lib/x86_64-linux-gnu/libc.so.6: version 'GLIBC_2.28' not found
npm ERR! peer dep missing: react@^17.0.0, required by react-query@3.39.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/blockquote&gt;

&lt;p&gt;His laptop: &lt;strong&gt;Node 14.18, npm 6.14, Ubuntu 22.04&lt;/strong&gt;. The staging server: &lt;strong&gt;Node 12.22, npm 5.6, CentOS 7&lt;/strong&gt;. Three days of debugging. Three days of &lt;em&gt;"but it works on my machine!"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This problem was not unique — every developer at every company was facing it. Docker was the solution.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dependency Hell — Real-World Examples
&lt;/h3&gt;

&lt;p&gt;The "Works on My Machine" problem is a symptom of a deeper disease: &lt;strong&gt;dependency hell&lt;/strong&gt;. Here are scenarios every developer has lived through:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;The Conflict&lt;/th&gt;
&lt;th&gt;Pain Level&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Two Python projects on same machine&lt;/td&gt;
&lt;td&gt;Project A needs Python 2.7, Project B needs Python 3.9. &lt;code&gt;pip install&lt;/code&gt; breaks both.&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;npm version conflict&lt;/td&gt;
&lt;td&gt;App needs npm 6 for legacy scripts, another needs npm 8. Switching breaks lockfiles.&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OpenSSL version mismatch&lt;/td&gt;
&lt;td&gt;Dev has OpenSSL 3.0, prod server has 1.1. Node.js crypto breaks silently.&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;System library versions (glibc)&lt;/td&gt;
&lt;td&gt;Compiled on Ubuntu 22, deployed on CentOS 7. Binary won't even load.&lt;/td&gt;
&lt;td&gt;Critical&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ruby gem native extensions&lt;/td&gt;
&lt;td&gt;Gem compiled against libxml2 on Mac, server has different libxml2. Segfault.&lt;/td&gt;
&lt;td&gt;Critical&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Think of Docker like sharing a React component with &lt;code&gt;node_modules&lt;/code&gt; — not just the code, but the &lt;em&gt;exact&lt;/em&gt; node version, npm version, and OS libraries. Docker packages your application together with its entire environment.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Deployment Nightmares Before Docker
&lt;/h3&gt;

&lt;p&gt;Before Docker, deploying an app to a new server looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# The legendary "deployment runbook" (50 pages of this)&lt;/span&gt;
ssh root@production-server-ip

&lt;span class="c"&gt;# Step 1: Install the right Node version (pray it's compatible)&lt;/span&gt;
curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://deb.nodesource.com/setup_14.x | bash -
apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; nodejs

&lt;span class="c"&gt;# Step 2: Hope npm is the right version&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; npm@6.14.15

&lt;span class="c"&gt;# Step 3: Clone app&lt;/span&gt;
git clone https://github.com/company/app.git /var/www/app
&lt;span class="nb"&gt;cd&lt;/span&gt; /var/www/app
npm &lt;span class="nb"&gt;install&lt;/span&gt;   &lt;span class="c"&gt;# Often fails here due to native modules&lt;/span&gt;

&lt;span class="c"&gt;# Step 4: Set up environment variables (done manually, often wrong)&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;DB_HOST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;192.168.1.100
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;NODE_ENV&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;production
&lt;span class="c"&gt;# (Someone always forgets one. App crashes at 2 AM)&lt;/span&gt;

&lt;span class="c"&gt;# Step 5: Set up PM2 or systemd (manual, error-prone)&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; pm2
pm2 start app.js &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="s2"&gt;"myapp"&lt;/span&gt;
pm2 startup  &lt;span class="c"&gt;# Different command on every distro&lt;/span&gt;

&lt;span class="c"&gt;# Step 6: Set up nginx (manual config, copy-paste errors)&lt;/span&gt;
&lt;span class="c"&gt;# ... 20 more steps ...&lt;/span&gt;

&lt;span class="c"&gt;# Total time: 4-8 hours per server&lt;/span&gt;
&lt;span class="c"&gt;# Success rate: ~60% first try&lt;/span&gt;
&lt;span class="c"&gt;# Consistency across servers: ZERO&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And if you need to deploy to 10 servers? Do all of this 10 times — and something different will happen each time. This is called &lt;strong&gt;configuration drift&lt;/strong&gt; — over time servers diverge from each other, and no one knows why production behaves differently.&lt;/p&gt;

&lt;h3&gt;
  
  
  The VM Tax — Resource Waste at Scale
&lt;/h3&gt;

&lt;p&gt;Companies tried solving this with Virtual Machines — one VM per application. But VMs have a massive overhead:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Resource&lt;/th&gt;
&lt;th&gt;Each VM Consumes&lt;/th&gt;
&lt;th&gt;10 Apps = 10 VMs&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;RAM&lt;/td&gt;
&lt;td&gt;2–4 GB minimum (OS alone)&lt;/td&gt;
&lt;td&gt;20–40 GB wasted on OS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Disk&lt;/td&gt;
&lt;td&gt;20–40 GB per VM image&lt;/td&gt;
&lt;td&gt;200–400 GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Boot time&lt;/td&gt;
&lt;td&gt;1–5 minutes&lt;/td&gt;
&lt;td&gt;Can't scale fast&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CPU&lt;/td&gt;
&lt;td&gt;Always-on OS processes&lt;/td&gt;
&lt;td&gt;Significant idle overhead&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A startup with 20 microservices using VMs wastes a minimum of 40 GB RAM just for operating systems. The same 20 services in Docker containers consume 2–5 GB of overhead — a dramatic difference that translates directly to cloud cost savings.&lt;/p&gt;

&lt;h3&gt;
  
  
  Docker's Shipping Container Analogy
&lt;/h3&gt;

&lt;p&gt;Before the 1950s, international shipping was chaos. Every ship had a different design, every port used different equipment, and cargo had to be repacked repeatedly. A shipment from Europe to America could take months, and costs were astronomical.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In 1956, Malcolm McLean invented the standardized shipping container.&lt;/strong&gt; One fixed size (20ft/40ft), standard corners, works on any ship, any port, any truck, any train. Load once in Shanghai — unload in Rotterdam without opening the box.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt; Global trade exploded. Shipping costs dropped 90%.&lt;/p&gt;

&lt;p&gt;Docker is the shipping container of software:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Shipping World&lt;/th&gt;
&lt;th&gt;Software World&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Different ships&lt;/td&gt;
&lt;td&gt;Different servers/OS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Different ports&lt;/td&gt;
&lt;td&gt;Different cloud providers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cargo re-packing&lt;/td&gt;
&lt;td&gt;Re-configuring for each environment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Standard container&lt;/td&gt;
&lt;td&gt;Docker container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Load once, ship anywhere&lt;/td&gt;
&lt;td&gt;Build once, run anywhere&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;McLean standardized trade&lt;/td&gt;
&lt;td&gt;Docker standardized deployment&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; When asked "Why do you use Docker?" in an interview — use the shipping container analogy. Then give concrete numbers: "Our deployment time went from 4 hours to 15 minutes. Configuration drift eliminated. Dev-prod parity achieved."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Why Every Company Adopted Docker
&lt;/h3&gt;

&lt;p&gt;By 2026, over &lt;strong&gt;80% of cloud workloads&lt;/strong&gt; run in containers. Here's why adoption was inevitable:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Dev-prod parity&lt;/strong&gt; — Same container in dev, test, staging, production. No more surprises.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Faster onboarding&lt;/strong&gt; — New developer joins? One command: &lt;code&gt;docker compose up&lt;/code&gt;. Ready in 5 minutes, not 2 days.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Microservices enablement&lt;/strong&gt; — Each service in its own container. Scale independently.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CI/CD becomes simple&lt;/strong&gt; — Build a container, test it, push to registry, deploy. Repeatable every time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost reduction&lt;/strong&gt; — Pack more apps per server. AWS/GCP bills drop 40–60%.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kubernetes compatibility&lt;/strong&gt; — Kubernetes orchestrates containers. You cannot use Kubernetes without containers.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Containers vs Virtual Machines
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Virtual Machines — The Apartment Building Analogy
&lt;/h3&gt;

&lt;p&gt;Think of a VM like separate apartments in a large building. Each apartment has its own kitchen, its own bathroom, its own living room. What happens in one apartment is completely invisible to the other.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;Virtual Machine&lt;/strong&gt; runs a complete operating system on top of a &lt;em&gt;hypervisor&lt;/em&gt; — software that emulates physical hardware. Each VM thinks it owns the whole computer.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Hypervisor&lt;/strong&gt; (VMware, VirtualBox, KVM, Hyper-V) sits between hardware and VMs&lt;/li&gt;
&lt;li&gt;Each VM gets a &lt;strong&gt;full Guest OS&lt;/strong&gt; (Windows, Ubuntu, etc.) — 1–4 GB just for the OS&lt;/li&gt;
&lt;li&gt;Hardware is &lt;strong&gt;emulated&lt;/strong&gt; — virtual CPU, virtual RAM, virtual disk&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Strong isolation&lt;/strong&gt; — a crash in VM1 doesn't affect VM2&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Boot time&lt;/strong&gt;: 1–5 minutes (full OS boot)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Containers — The Shared House Analogy
&lt;/h3&gt;

&lt;p&gt;Think of containers like rooms in a shared house. You share the same kitchen and water supply, but each person has their own private room and lives independently.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;Container&lt;/strong&gt; shares the host OS kernel but runs in an isolated process namespace. No hardware emulation, no duplicate OS.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Shared kernel&lt;/strong&gt; — uses the host's Linux kernel directly&lt;/li&gt;
&lt;li&gt;Each container gets an &lt;strong&gt;isolated view&lt;/strong&gt; of processes, network, filesystem&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No hypervisor overhead&lt;/strong&gt; — near-native performance&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lightweight&lt;/strong&gt; — MBs instead of GBs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Boot time&lt;/strong&gt;: Milliseconds to seconds&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;
Containers skip the Guest OS layer that VMs require, cutting overhead from gigabytes per app to near zero.





&lt;h3&gt;
  
  
  Detailed Comparison Table
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Virtual Machine&lt;/th&gt;
&lt;th&gt;Container&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Startup Time&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1–5 minutes (full OS boot)&lt;/td&gt;
&lt;td&gt;Seconds (process start)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Size&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;2–40 GB per VM image&lt;/td&gt;
&lt;td&gt;10 MB–1 GB (app + libs only)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;OS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Full Guest OS per VM&lt;/td&gt;
&lt;td&gt;Shares Host OS Kernel&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Isolation Level&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Hardware-level (very strong)&lt;/td&gt;
&lt;td&gt;Process-level (strong, not absolute)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Performance Overhead&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;5–15% CPU/RAM overhead&lt;/td&gt;
&lt;td&gt;&amp;lt;1–2% overhead (near-native)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Portability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Limited (hypervisor-specific)&lt;/td&gt;
&lt;td&gt;Excellent (any Linux/Mac/Win with Docker)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Resource Usage&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Heavy (duplicate OS per VM)&lt;/td&gt;
&lt;td&gt;Light (shared kernel)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Density (per server)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;5–20 VMs typical&lt;/td&gt;
&lt;td&gt;50–500 containers typical&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Security Isolation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Very strong (hardware boundary)&lt;/td&gt;
&lt;td&gt;Good (namespaces + cgroups)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Best Use Case&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Legacy apps, strong isolation, Windows workloads&lt;/td&gt;
&lt;td&gt;Microservices, CI/CD, cloud-native apps&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Real-world production numbers: Server utilization jumped from 30–40% (with VMs) to 80%+ (with containers). Deployment time dropped from 45 minutes to 8 minutes. Auto-scaling: containers spin up in 3 seconds vs 4 minutes for VMs — critical during peak load events.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  When to Use VM vs Container
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Use VM When...&lt;/th&gt;
&lt;th&gt;Use Container When...&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Running Windows workloads on Linux host&lt;/td&gt;
&lt;td&gt;Running microservices/APIs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Need kernel-level isolation (security-critical)&lt;/td&gt;
&lt;td&gt;CI/CD pipelines (build, test, deploy)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Legacy apps that need specific OS versions&lt;/td&gt;
&lt;td&gt;Stateless applications&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Running untrusted code (multi-tenant SaaS)&lt;/td&gt;
&lt;td&gt;Development environments&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Database servers needing dedicated resources&lt;/td&gt;
&lt;td&gt;Auto-scaling workloads&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; In enterprise environments, both are used together. VMs run Docker. In the cloud, an EC2 VM runs Docker containers on top. This is not a binary choice — they are complementary technologies.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  What is Docker?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Origin Story — From DotCloud to Changing the World
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;2008:&lt;/strong&gt; Solomon Hykes co-founds DotCloud — a Platform-as-a-Service (PaaS) company in San Francisco. They're building tools to deploy customer apps on their platform, and internally they develop a container management tool.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;March 21, 2013:&lt;/strong&gt; PyCon, Santa Clara. Hykes gives a 5-minute lightning talk called &lt;em&gt;"The Future of Linux Containers."&lt;/em&gt; He demos Docker — their internal tool they're open-sourcing. The audience is small. The impact: &lt;strong&gt;massive&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A 5-minute talk at a Python conference launched a technology that today runs in every data center in the world. That's what happens when the right solution meets the right problem at the right time.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;2013:&lt;/strong&gt; Docker open-sourced, goes viral on GitHub&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;2014:&lt;/strong&gt; Google, Microsoft, Amazon begin supporting Docker&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;2015:&lt;/strong&gt; Docker Inc. raises $95M, valuation $1B+&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;2016:&lt;/strong&gt; Docker Swarm, Docker Compose GA. Enterprise adoption explodes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;2017:&lt;/strong&gt; Kubernetes wins the orchestration wars, but Docker remains the build standard&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;2019:&lt;/strong&gt; Docker Inc. sells its enterprise business to Mirantis, refocuses on developers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;2023+:&lt;/strong&gt; Docker Desktop 4.x, Docker Scout (security), AI-powered features&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;2026:&lt;/strong&gt; Docker is as fundamental as Git — you cannot be a backend/DevOps engineer without it&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Docker vs Other Container Runtimes
&lt;/h3&gt;

&lt;p&gt;Docker is not just one tool — it is an ecosystem. But the container runtime market has other players:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Runtime&lt;/th&gt;
&lt;th&gt;Used By&lt;/th&gt;
&lt;th&gt;Key Feature&lt;/th&gt;
&lt;th&gt;Should You Learn?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Docker&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Developers, small teams, CI/CD&lt;/td&gt;
&lt;td&gt;Best Developer Experience (DX), most popular, huge ecosystem&lt;/td&gt;
&lt;td&gt;Yes, first priority&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;containerd&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Kubernetes production clusters&lt;/td&gt;
&lt;td&gt;Lightweight production runtime, CNCF graduated project&lt;/td&gt;
&lt;td&gt;Learn after Docker&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Podman&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Red Hat / Fedora users, security-conscious teams&lt;/td&gt;
&lt;td&gt;Rootless (no daemon), daemonless, Docker-compatible CLI&lt;/td&gt;
&lt;td&gt;Be aware of it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CRI-O&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;OpenShift, Kubernetes&lt;/td&gt;
&lt;td&gt;Minimal footprint, built for Kubernetes CRI interface&lt;/td&gt;
&lt;td&gt;Be aware of it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;runc&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Low-level, all runtimes use it&lt;/td&gt;
&lt;td&gt;OCI reference implementation, the actual container runner&lt;/td&gt;
&lt;td&gt;Deep-dive later&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; When asked "What is the difference between Docker and containerd?" — say this: Docker is the full toolchain (build + run + push + compose). containerd is just the runtime that Kubernetes uses to actually run containers. Docker internally uses containerd. Think of it as: Docker = Full Car, containerd = The Engine.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Docker's Place in the DevOps Stack
&lt;/h3&gt;

&lt;p&gt;Docker is the &lt;strong&gt;central connective tissue&lt;/strong&gt; of the modern DevOps pipeline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Code (React/Node/Python) → Build (docker build / Dockerfile) → Test (docker run test suite) → Registry (docker push / ECR / Docker Hub) → Deploy (ECS / Kubernetes / docker compose up)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Career Impact
&lt;/h3&gt;

&lt;p&gt;Docker knowledge significantly expands your career options:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Skill Level&lt;/th&gt;
&lt;th&gt;Typical Role&lt;/th&gt;
&lt;th&gt;US (USD)&lt;/th&gt;
&lt;th&gt;India (INR)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Without Docker&lt;/td&gt;
&lt;td&gt;Junior/Mid Developer (Frontend/Backend)&lt;/td&gt;
&lt;td&gt;$80k–120k&lt;/td&gt;
&lt;td&gt;₹15–20 LPA&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Docker + Kubernetes&lt;/td&gt;
&lt;td&gt;DevOps / SRE (2–5 yrs exp)&lt;/td&gt;
&lt;td&gt;$120k–170k&lt;/td&gt;
&lt;td&gt;₹30–50 LPA&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Docker + AWS + K8s + CI/CD&lt;/td&gt;
&lt;td&gt;Senior DevOps / Staff SRE (5+ yrs exp)&lt;/td&gt;
&lt;td&gt;$170k+&lt;/td&gt;
&lt;td&gt;₹50+ LPA&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Ranges are rough 2026 signals and vary widely by region, company size, and cost of living.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A React/frontend background is a unique advantage in DevOps — engineers who understand both frontend containerization and backend infrastructure are in high demand.&lt;/p&gt;

&lt;h3&gt;
  
  
  Docker By The Numbers — 2026
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;13M+&lt;/strong&gt; images on Docker Hub&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;13B+&lt;/strong&gt; pulls per month&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;20M+&lt;/strong&gt; Docker Desktop users&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;80%&lt;/strong&gt; of cloud workloads containerized&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Containerization Concepts — The Linux Magic
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Foundation — Why Linux?
&lt;/h3&gt;

&lt;p&gt;Docker's magic comes from three specific Linux kernel features. Understanding them is what separates a senior engineer from a beginner — and this knowledge is invaluable in interviews when asked "How does Docker work internally?"&lt;/p&gt;

&lt;p&gt;Docker containers are not magic — they're &lt;strong&gt;three Linux kernel features&lt;/strong&gt; working together:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Namespaces&lt;/strong&gt; — What a container can &lt;em&gt;see&lt;/em&gt; (isolation of view)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cgroups&lt;/strong&gt; — What a container can &lt;em&gt;use&lt;/em&gt; (resource limits)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Union Filesystem (OverlayFS)&lt;/strong&gt; — How a container's &lt;em&gt;filesystem&lt;/em&gt; is built&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Linux Namespaces — The Isolation Magic
&lt;/h3&gt;

&lt;p&gt;A namespace makes a container feel like it is the only process on the machine — like an actor on a film set who is made to feel they are actually in a royal palace, when they are actually standing on a soundstage.&lt;/p&gt;

&lt;p&gt;Linux has &lt;strong&gt;7 types of namespaces&lt;/strong&gt;. Docker uses 6 of them:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PID Namespace — Process ID Isolation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The container believes it has its own process tree. Container's PID 1 = Host's PID 5678.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Inside container: sees its own process tree&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;ps aux
PID   USER  CMD
1     root  node server.js   ← thinks it&lt;span class="s1"&gt;'s PID 1!
12    root  /bin/sh

# On host: sees the real PID
$ ps aux | grep node
5678  root  node server.js   ← actually PID 5678
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The container cannot see parent processes. Completely isolated process tree.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NET Namespace — Network Isolation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Each container gets its own network interfaces, IP address, routing table, and firewall rules.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Container 1: has its own IP&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker &lt;span class="nb"&gt;exec &lt;/span&gt;container1 ip addr
172.17.0.2/16   ← container&lt;span class="s1"&gt;'s IP

# Container 2: different IP
$ docker exec container2 ip addr
172.17.0.3/16   ← different IP, same host

# Host: sees docker0 bridge
$ ip addr show docker0
172.17.0.1/16   ← bridge connects them
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;MNT Namespace — Filesystem Isolation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The container has its own filesystem view. It appears to have a complete Linux filesystem — &lt;code&gt;/etc&lt;/code&gt;, &lt;code&gt;/usr&lt;/code&gt;, &lt;code&gt;/var&lt;/code&gt; all separate.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Container sees its own filesystem&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker &lt;span class="nb"&gt;exec &lt;/span&gt;mycontainer &lt;span class="nb"&gt;ls&lt;/span&gt; /
bin  dev  etc  home  lib  proc
root  sys  tmp  usr  var  app

&lt;span class="c"&gt;# Host files are NOT visible in container&lt;/span&gt;
&lt;span class="c"&gt;# (unless explicitly mounted with -v)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;UTS Namespace — Hostname Isolation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Each container can have its own hostname, independent of the host machine.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Set custom hostname for container&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;--hostname&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;myapp-prod ubuntu &lt;span class="nb"&gt;hostname
&lt;/span&gt;myapp-prod

&lt;span class="c"&gt;# Host machine sees its own hostname&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;hostname
&lt;/span&gt;prod-server-01
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;USER Namespace — User ID Mapping&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Maps the container's root user to an unprivileged host user. Container "root" is NOT host root — important for security!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Container root → host user 1000&lt;/span&gt;
&lt;span class="c"&gt;# Even if container is "root",&lt;/span&gt;
&lt;span class="c"&gt;# on host it's unprivileged UID 1000&lt;/span&gt;
&lt;span class="c"&gt;# Security: container escape doesn't&lt;/span&gt;
&lt;span class="c"&gt;# give attacker host root access&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;IPC Namespace — Inter-Process Communication Isolation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Containers cannot access each other's shared memory, semaphores, or message queues.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Share IPC namespace between containers&lt;/span&gt;
&lt;span class="c"&gt;# (for high-performance apps like Redis)&lt;/span&gt;
docker run &lt;span class="nt"&gt;--ipc&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;container:redis-container my-app

&lt;span class="c"&gt;# Default: isolated IPC per container&lt;/span&gt;
&lt;span class="c"&gt;# No shared memory leaks between apps&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Cgroups — Resource Control
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Control Groups (cgroups)&lt;/strong&gt; control &lt;em&gt;how much&lt;/em&gt; resource a container can use. If namespaces define what a container can see, cgroups define how much it can consume.&lt;/p&gt;

&lt;p&gt;Think of cgroups as a &lt;strong&gt;resource budget&lt;/strong&gt; per container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Memory limit: container cannot use more than 512MB&lt;/span&gt;
docker run &lt;span class="nt"&gt;--memory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;512m nginx

&lt;span class="c"&gt;# CPU limit: container gets max 50% of one CPU core&lt;/span&gt;
docker run &lt;span class="nt"&gt;--cpus&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0.5 nginx

&lt;span class="c"&gt;# Both together (production-ready)&lt;/span&gt;
docker run &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--memory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;512m &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--memory-swap&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;512m &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--cpus&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0.5 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--cpu-shares&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;512 &lt;span class="se"&gt;\&lt;/span&gt;
  nginx

&lt;span class="c"&gt;# I/O limits (read/write speed)&lt;/span&gt;
docker run &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--device-read-bps&lt;/span&gt; /dev/sda:100mb &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--device-write-bps&lt;/span&gt; /dev/sda:50mb &lt;span class="se"&gt;\&lt;/span&gt;
  nginx

&lt;span class="c"&gt;# See current container resource usage&lt;/span&gt;
docker stats &lt;span class="nt"&gt;--no-stream&lt;/span&gt;

&lt;span class="c"&gt;# Output:&lt;/span&gt;
&lt;span class="c"&gt;# CONTAINER   CPU %   MEM USAGE / LIMIT    NET I/O&lt;/span&gt;
&lt;span class="c"&gt;# nginx       0.1%    12MiB / 512MiB       1.2kB / 0B&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Cgroup Subsystem&lt;/th&gt;
&lt;th&gt;What It Controls&lt;/th&gt;
&lt;th&gt;Docker Flag&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;memory&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Max RAM usage, OOM killer behavior&lt;/td&gt;
&lt;td&gt;&lt;code&gt;--memory=512m&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cpu&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;CPU time allocation&lt;/td&gt;
&lt;td&gt;&lt;code&gt;--cpus=0.5&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cpuset&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Which CPU cores to use&lt;/td&gt;
&lt;td&gt;&lt;code&gt;--cpuset-cpus=0,1&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;blkio&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Block I/O limits (disk read/write)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;--device-read-bps&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;net_cls&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Network bandwidth tagging&lt;/td&gt;
&lt;td&gt;(via tc/iptables)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pids&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Max number of processes in container&lt;/td&gt;
&lt;td&gt;&lt;code&gt;--pids-limit=100&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; Always set memory and CPU limits on every production container. Without limits, a runaway container can crash the entire host server — the "noisy neighbour" problem. During peak traffic, if one container hogs CPU, all other containers will slow down. Limits protect all containers on the host.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Union Filesystem — OverlayFS
&lt;/h3&gt;

&lt;p&gt;A Docker image is made of &lt;strong&gt;read-only layers&lt;/strong&gt;. When you run a container, Docker adds a thin &lt;strong&gt;writable layer&lt;/strong&gt; on top. This is OverlayFS (Overlay Filesystem).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Example Dockerfile - creates layers&lt;/span&gt;
FROM ubuntu:22.04            &lt;span class="c"&gt;# Layer 1: Base Ubuntu (~70MB)&lt;/span&gt;
RUN apt-get update           &lt;span class="c"&gt;# Layer 2: Package index (~20MB)&lt;/span&gt;
RUN apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; curl  &lt;span class="c"&gt;# Layer 3: curl binary (~5MB)&lt;/span&gt;
COPY app.js /app/            &lt;span class="c"&gt;# Layer 4: Your app (~1MB)&lt;/span&gt;
CMD &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"node"&lt;/span&gt;, &lt;span class="s2"&gt;"app.js"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;       &lt;span class="c"&gt;# Layer 5: Metadata only&lt;/span&gt;

&lt;span class="c"&gt;# See the layers of an image&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker &lt;span class="nb"&gt;history &lt;/span&gt;my-node-app
IMAGE         CREATED      SIZE     COMMENT
a1b2c3d4      2 hours ago  1.2MB    COPY app.js
f5e6d7c8      2 hours ago  5.1MB    apt-get &lt;span class="nb"&gt;install &lt;/span&gt;curl
g9h0i1j2      2 hours ago  22MB     apt-get update
ubuntu:22.04  2 weeks ago  72MB     base layer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Copy-on-Write (CoW) — The Secret Sauce&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When a container &lt;strong&gt;modifies&lt;/strong&gt; a file from a read-only layer:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Docker &lt;em&gt;copies&lt;/em&gt; that file to the writable layer&lt;/li&gt;
&lt;li&gt;Makes the modification in the writable layer&lt;/li&gt;
&lt;li&gt;The original read-only layer is &lt;strong&gt;untouched&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;100 containers sharing the same image = base layers shared, only diffs stored&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If 100 containers are using the same nginx image, there is still only one copy on disk. 100x efficiency!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# See OverlayFS in action on Linux host&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker inspect mycontainer | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-A&lt;/span&gt; 10 GraphDriver
&lt;span class="s2"&gt;"GraphDriver"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"Data"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"LowerDir"&lt;/span&gt;: &lt;span class="s2"&gt;"/var/lib/docker/overlay2/abc123/diff:
                 /var/lib/docker/overlay2/def456/diff"&lt;/span&gt;,
    &lt;span class="s2"&gt;"MergedDir"&lt;/span&gt;: &lt;span class="s2"&gt;"/var/lib/docker/overlay2/xyz789/merged"&lt;/span&gt;,
    &lt;span class="s2"&gt;"UpperDir"&lt;/span&gt;:  &lt;span class="s2"&gt;"/var/lib/docker/overlay2/xyz789/diff"&lt;/span&gt;,
    &lt;span class="s2"&gt;"WorkDir"&lt;/span&gt;:   &lt;span class="s2"&gt;"/var/lib/docker/overlay2/xyz789/work"&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="c"&gt;# LowerDir = read-only image layers (stacked)&lt;/span&gt;
&lt;span class="c"&gt;# UpperDir = writable container layer (your changes)&lt;/span&gt;
&lt;span class="c"&gt;# MergedDir = what container sees (unified view)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note (Senior Engineer Interview):&lt;/strong&gt; "Both Docker containers and VMs provide isolation — so why are containers considered less secure?" Answer: VMs provide hardware-level isolation (hypervisor = hard boundary). Containers only use Linux kernel features — if there is a kernel vulnerability (like Dirty COW, runc CVE), container escape is possible. This is why multi-tenant environments running untrusted code prefer VMs or gVisor/Kata Containers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tip (Quick Summary):&lt;/strong&gt; Docker container = Linux process + Namespaces (isolation of view: can't see others' processes, network, files) + Cgroups (resource limits: max CPU/RAM) + OverlayFS (layered filesystem: shared base + writable top). Everything else — Dockerfile, docker-compose, Kubernetes — is built on top of these three.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Docker Architecture Deep Dive
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Big Picture
&lt;/h3&gt;

&lt;p&gt;Docker follows a client-server architecture. When you type &lt;code&gt;docker run nginx&lt;/code&gt;, a lot is happening under the hood — think of it as an assembly line where each component performs a specific job.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;
The Docker CLI is a thin REST client; all heavy lifting happens inside the Docker daemon, which coordinates containerd and runc to run containers and communicates with registries for image storage.





&lt;h3&gt;
  
  
  1. Docker Client (CLI)
&lt;/h3&gt;

&lt;p&gt;The Docker Client is the tool you use — the &lt;code&gt;docker&lt;/code&gt; command. When you type &lt;code&gt;docker run nginx&lt;/code&gt;, the client only translates CLI arguments into a REST API call and sends it to the Docker Daemon. The client itself does nothing else — it is just a translator.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Docker CLI is just making REST API calls to the daemon&lt;/span&gt;
&lt;span class="c"&gt;# You can curl the Docker socket directly&lt;/span&gt;

&lt;span class="c"&gt;# List running containers (same as docker ps)&lt;/span&gt;
curl &lt;span class="nt"&gt;--unix-socket&lt;/span&gt; /var/run/docker.sock http://localhost/v1.43/containers/json

&lt;span class="c"&gt;# Start a container via API&lt;/span&gt;
curl &lt;span class="nt"&gt;--unix-socket&lt;/span&gt; /var/run/docker.sock &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"Image": "nginx"}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  http://localhost/v1.43/containers/create

&lt;span class="c"&gt;# Check Docker version via API&lt;/span&gt;
curl &lt;span class="nt"&gt;--unix-socket&lt;/span&gt; /var/run/docker.sock http://localhost/v1.43/version | jq

&lt;span class="c"&gt;# See all API endpoints&lt;/span&gt;
curl &lt;span class="nt"&gt;--unix-socket&lt;/span&gt; /var/run/docker.sock http://localhost/v1.43/_ping
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; You can control a remote machine's Docker: &lt;code&gt;export DOCKER_HOST=tcp://192.168.1.100:2376&lt;/code&gt;, then run normal docker commands. In production, always use TLS (port 2376), never plain port 2375!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  2. Docker Daemon (dockerd)
&lt;/h3&gt;

&lt;p&gt;The Docker Daemon (dockerd) is the background process that actually does the work. It accepts REST API requests and manages containers, images, networks, and volumes.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Details&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Default Socket&lt;/td&gt;
&lt;td&gt;&lt;code&gt;unix:///var/run/docker.sock&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TCP (TLS)&lt;/td&gt;
&lt;td&gt;Port 2376 (production)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TCP (No TLS)&lt;/td&gt;
&lt;td&gt;Port 2375 (NEVER in production!)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Config File&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/etc/docker/daemon.json&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Logs&lt;/td&gt;
&lt;td&gt;&lt;code&gt;journalctl -u docker.service&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Restart&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo systemctl restart docker&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  3. Docker Registry
&lt;/h3&gt;

&lt;p&gt;Registry is where images are stored. Docker Hub is the default public registry, but enterprises use private registries:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Docker Hub&lt;/strong&gt; — Default public registry. Free tier available. &lt;code&gt;docker.io/library/nginx&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS ECR&lt;/strong&gt; — Amazon Elastic Container Registry. IAM integration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GCR / GAR&lt;/strong&gt; — Google Container/Artifact Registry. Seamless with GKE.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GHCR&lt;/strong&gt; — GitHub Container Registry. Tight CI/CD integration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Harbor&lt;/strong&gt; — Open-source enterprise registry. Self-hosted. Vulnerability scanning built-in.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Artifactory&lt;/strong&gt; — JFrog's universal artifact manager. Docker + Maven + npm in one place.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. containerd — High-Level Runtime
&lt;/h3&gt;

&lt;p&gt;containerd is a CNCF project that sits between the Docker Daemon and actual containers. It manages image pulling, storage, and container lifecycle. Kubernetes also uses containerd directly (without Docker).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# containerd has its own CLI: ctr&lt;/span&gt;
&lt;span class="c"&gt;# Note: operates in a different namespace from Docker&lt;/span&gt;

&lt;span class="c"&gt;# List images in containerd&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;ctr images &lt;span class="nb"&gt;ls&lt;/span&gt;

&lt;span class="c"&gt;# List containers&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;ctr containers &lt;span class="nb"&gt;ls&lt;/span&gt;

&lt;span class="c"&gt;# Pull image directly via containerd&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;ctr images pull docker.io/library/nginx:latest

&lt;span class="c"&gt;# Run container via containerd (low-level)&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;ctr run docker.io/library/nginx:latest mynginx

&lt;span class="c"&gt;# containerd socket&lt;/span&gt;
&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt; /run/containerd/containerd.sock

&lt;span class="c"&gt;# Check containerd status&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl status containerd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Kubernetes v1.24+ removed Docker support. Kubernetes now talks directly to containerd via CRI (Container Runtime Interface). Docker's "removal" was only the removal of the Docker daemon — containers still run the same way because containerd and runc are the same underneath.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  5. runc — Low-Level Runtime
&lt;/h3&gt;

&lt;p&gt;runc is the reference implementation of the OCI Runtime Specification. It directly makes Linux kernel syscalls — creates namespaces, sets up cgroups, and starts the process. It is the actual tool that makes a "container" exist.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# runc version check&lt;/span&gt;
runc &lt;span class="nt"&gt;--version&lt;/span&gt;

&lt;span class="c"&gt;# runc is not normally used directly&lt;/span&gt;
&lt;span class="c"&gt;# But for understanding: containerd calls runc like this:&lt;/span&gt;
&lt;span class="c"&gt;# runc create --bundle /path/to/bundle container_id&lt;/span&gt;
&lt;span class="c"&gt;# runc start container_id&lt;/span&gt;
&lt;span class="c"&gt;# runc delete container_id&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Complete Flow: docker run nginx
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;You type &lt;code&gt;docker run nginx&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Docker CLI → REST API call → Docker daemon (via /var/run/docker.sock)&lt;/li&gt;
&lt;li&gt;dockerd checks: is the image available locally? No → pull from Registry&lt;/li&gt;
&lt;li&gt;dockerd → tells containerd to start the container&lt;/li&gt;
&lt;li&gt;containerd → spawns a containerd-shim process&lt;/li&gt;
&lt;li&gt;containerd-shim → calls runc&lt;/li&gt;
&lt;li&gt;runc → makes Linux kernel syscalls (namespaces + cgroups setup)&lt;/li&gt;
&lt;li&gt;Container process (nginx) starts&lt;/li&gt;
&lt;li&gt;runc exits; containerd-shim stays alive (for I/O)&lt;/li&gt;
&lt;li&gt;Container keeps running even on daemon restart&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Docker Installation on Ubuntu 24.04 (Complete Guide)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Prerequisites Check
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; Check if Docker is already installed before starting. If an old version exists, remove it first: &lt;code&gt;sudo apt remove docker docker-engine docker.io containerd runc&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# System info check&lt;/span&gt;
&lt;span class="nb"&gt;uname&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt;
lsb_release &lt;span class="nt"&gt;-a&lt;/span&gt;
&lt;span class="nb"&gt;whoami&lt;/span&gt;

&lt;span class="c"&gt;# Check if Docker already installed&lt;/span&gt;
docker &lt;span class="nt"&gt;--version&lt;/span&gt; 2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Docker not installed"&lt;/span&gt;

&lt;span class="c"&gt;# Remove old versions if they exist&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt remove &lt;span class="nt"&gt;-y&lt;/span&gt; docker docker-engine docker.io containerd runc 2&amp;gt;/dev/null
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt autoremove &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 1: System Update
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;apt upgrade &lt;span class="nt"&gt;-y&lt;/span&gt;

&lt;span class="c"&gt;# If the kernel was updated, reboot (optional but recommended)&lt;/span&gt;
&lt;span class="c"&gt;# sudo reboot&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Install Prerequisites
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  ca-certificates &lt;span class="se"&gt;\&lt;/span&gt;
  curl &lt;span class="se"&gt;\&lt;/span&gt;
  gnupg &lt;span class="se"&gt;\&lt;/span&gt;
  lsb-release

&lt;span class="c"&gt;# Verify install&lt;/span&gt;
curl &lt;span class="nt"&gt;--version&lt;/span&gt;
gpg &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Add Docker's Official GPG Key
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create keyrings directory&lt;/span&gt;
&lt;span class="nb"&gt;sudo install&lt;/span&gt; &lt;span class="nt"&gt;-m&lt;/span&gt; 0755 &lt;span class="nt"&gt;-d&lt;/span&gt; /etc/apt/keyrings

&lt;span class="c"&gt;# Download Docker's GPG key and convert to binary format&lt;/span&gt;
curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://download.docker.com/linux/ubuntu/gpg | &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nb"&gt;sudo &lt;/span&gt;gpg &lt;span class="nt"&gt;--dearmor&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /etc/apt/keyrings/docker.gpg

&lt;span class="c"&gt;# Set read permissions&lt;/span&gt;
&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;a+r /etc/apt/keyrings/docker.gpg

&lt;span class="c"&gt;# Verify key&lt;/span&gt;
gpg &lt;span class="nt"&gt;--show-keys&lt;/span&gt; /etc/apt/keyrings/docker.gpg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The GPG key ensures packages come from Docker's official server and have not been intercepted. This protects against man-in-the-middle attacks. Always verify packages before installing.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Step 4: Add Docker Repository
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"deb [arch="&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;dpkg &lt;span class="nt"&gt;--print-architecture&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;" signed-by=/etc/apt/keyrings/docker.gpg] &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
  https://download.docker.com/linux/ubuntu &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
  "&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; /etc/os-release &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$VERSION_CODENAME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;" stable"&lt;/span&gt; | &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nb"&gt;sudo tee&lt;/span&gt; /etc/apt/sources.list.d/docker.list &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null

&lt;span class="c"&gt;# Verify repository was added&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; /etc/apt/sources.list.d/docker.list

&lt;span class="c"&gt;# Expected output:&lt;/span&gt;
&lt;span class="c"&gt;# deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.gpg]&lt;/span&gt;
&lt;span class="c"&gt;#   https://download.docker.com/linux/ubuntu noble stable&lt;/span&gt;

&lt;span class="c"&gt;# Update apt cache with new repository&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 5: Install Docker Engine
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  docker-ce &lt;span class="se"&gt;\&lt;/span&gt;
  docker-ce-cli &lt;span class="se"&gt;\&lt;/span&gt;
  containerd.io &lt;span class="se"&gt;\&lt;/span&gt;
  docker-buildx-plugin &lt;span class="se"&gt;\&lt;/span&gt;
  docker-compose-plugin

&lt;span class="c"&gt;# Package breakdown:&lt;/span&gt;
&lt;span class="c"&gt;# docker-ce              → Docker Community Edition (daemon)&lt;/span&gt;
&lt;span class="c"&gt;# docker-ce-cli          → Docker CLI (the 'docker' command)&lt;/span&gt;
&lt;span class="c"&gt;# containerd.io          → containerd runtime&lt;/span&gt;
&lt;span class="c"&gt;# docker-buildx-plugin   → BuildKit for advanced image building&lt;/span&gt;
&lt;span class="c"&gt;# docker-compose-plugin  → Docker Compose v2 (docker compose, not docker-compose)&lt;/span&gt;

&lt;span class="c"&gt;# To install a specific version:&lt;/span&gt;
&lt;span class="c"&gt;# List available versions&lt;/span&gt;
apt-cache madison docker-ce

&lt;span class="c"&gt;# Install specific version&lt;/span&gt;
&lt;span class="c"&gt;# sudo apt install docker-ce=5:26.0.0-1~ubuntu.24.04~noble \&lt;/span&gt;
&lt;span class="c"&gt;#   docker-ce-cli=5:26.0.0-1~ubuntu.24.04~noble \&lt;/span&gt;
&lt;span class="c"&gt;#   containerd.io docker-buildx-plugin docker-compose-plugin&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 6: Verify Installation
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;docker run hello-world

&lt;span class="c"&gt;# Expected output:&lt;/span&gt;
&lt;span class="c"&gt;# Hello from Docker!&lt;/span&gt;
&lt;span class="c"&gt;# This message shows that your installation appears to be working correctly.&lt;/span&gt;

&lt;span class="c"&gt;# Service status check&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl status docker
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl status containerd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 7: Post-Installation — Run Without sudo
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create 'docker' group (usually already exists)&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;groupadd docker 2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Group already exists"&lt;/span&gt;

&lt;span class="c"&gt;# Add current user to docker group&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;usermod &lt;span class="nt"&gt;-aG&lt;/span&gt; docker &lt;span class="nv"&gt;$USER&lt;/span&gt;

&lt;span class="c"&gt;# Apply group changes (no logout/login needed for this session)&lt;/span&gt;
newgrp docker

&lt;span class="c"&gt;# Verify — run docker without sudo&lt;/span&gt;
docker run hello-world

&lt;span class="c"&gt;# Permanent verification&lt;/span&gt;
&lt;span class="nb"&gt;groups&lt;/span&gt; &lt;span class="nv"&gt;$USER&lt;/span&gt;
&lt;span class="c"&gt;# Output should include 'docker'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; Adding a user to the docker group is practically equivalent to giving root access. Through the Docker socket (&lt;code&gt;/var/run/docker.sock&lt;/code&gt;), anyone can mount a volume and access the host filesystem. Add users carefully on production servers. Rootless Docker is a more secure option.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Step 8: Enable Docker on Boot
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;docker.service
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;containerd.service

&lt;span class="c"&gt;# Manual start/stop/restart&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl start docker
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl stop docker
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl restart docker

&lt;span class="c"&gt;# View logs&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;journalctl &lt;span class="nt"&gt;-u&lt;/span&gt; docker.service &lt;span class="nt"&gt;-f&lt;/span&gt;      &lt;span class="c"&gt;# Live logs&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;journalctl &lt;span class="nt"&gt;-u&lt;/span&gt; docker.service &lt;span class="nt"&gt;--since&lt;/span&gt; &lt;span class="s2"&gt;"1 hour ago"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Post-Installation Verification
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Version info&lt;/span&gt;
docker &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;span class="c"&gt;# Docker version 26.1.4, build 5650f9b&lt;/span&gt;

docker compose version
&lt;span class="c"&gt;# Docker Compose version v2.27.1&lt;/span&gt;

&lt;span class="c"&gt;# System info (detailed)&lt;/span&gt;
docker info

&lt;span class="c"&gt;# Test container&lt;/span&gt;
docker run hello-world

&lt;span class="c"&gt;# Interactive ubuntu container&lt;/span&gt;
docker run &lt;span class="nt"&gt;-it&lt;/span&gt; ubuntu bash

&lt;span class="c"&gt;# Nginx web server test&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:80 nginx
curl http://localhost:8080

&lt;span class="c"&gt;# Cleanup&lt;/span&gt;
docker stop &lt;span class="si"&gt;$(&lt;/span&gt;docker ps &lt;span class="nt"&gt;-q&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
docker system prune &lt;span class="nt"&gt;-f&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Common Installation Errors &amp;amp; Fixes
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Error&lt;/th&gt;
&lt;th&gt;Cause&lt;/th&gt;
&lt;th&gt;Fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;permission denied while trying to connect&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;User not in docker group&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo usermod -aG docker $USER &amp;amp;&amp;amp; newgrp docker&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Cannot connect to the Docker daemon&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;dockerd not running&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo systemctl start docker&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dial unix /var/run/docker.sock: connect: no such file&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Docker not installed or not started&lt;/td&gt;
&lt;td&gt;Install Docker OR &lt;code&gt;sudo systemctl start docker&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Failed to connect to bus: No such file&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;systemd not available (WSL/container)&lt;/td&gt;
&lt;td&gt;Logout and login again, or &lt;code&gt;sudo service docker start&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;E: Package 'docker-ce' has no installation candidate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Repository not added&lt;/td&gt;
&lt;td&gt;Redo steps 3-4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;toomanyrequests&lt;/code&gt; from Docker Hub&lt;/td&gt;
&lt;td&gt;Rate limit hit (unauthenticated)&lt;/td&gt;
&lt;td&gt;Run &lt;code&gt;docker login&lt;/code&gt; or use a mirror&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Docker Rootless Mode
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install prerequisites&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; uidmap

&lt;span class="c"&gt;# Install rootless Docker (as regular user, NOT sudo)&lt;/span&gt;
dockerd-rootless-setuptool.sh &lt;span class="nb"&gt;install&lt;/span&gt;

&lt;span class="c"&gt;# Set environment variables&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;DOCKER_HOST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;unix://&lt;span class="nv"&gt;$XDG_RUNTIME_DIR&lt;/span&gt;/docker.sock
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;/bin:&lt;span class="nv"&gt;$PATH&lt;/span&gt;

&lt;span class="c"&gt;# Add to ~/.bashrc for persistence&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/docker.sock'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; ~/.bashrc
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'export PATH=$HOME/bin:$PATH'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; ~/.bashrc

&lt;span class="c"&gt;# Enable service (user-level systemd)&lt;/span&gt;
systemctl &lt;span class="nt"&gt;--user&lt;/span&gt; &lt;span class="nb"&gt;enable &lt;/span&gt;docker
systemctl &lt;span class="nt"&gt;--user&lt;/span&gt; start docker

&lt;span class="c"&gt;# Verify&lt;/span&gt;
docker info | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; rootless
&lt;span class="c"&gt;# Should show: rootless: true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; In security-critical environments, use rootless Docker or Podman. In rootless mode, the impact of container breakout attacks is drastically reduced because the daemon is not root. In Kubernetes environments, configure &lt;code&gt;securityContext&lt;/code&gt; with non-root users.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Installation on RHEL / CentOS / Rocky Linux
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Step 1: Install yum-utils&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;yum &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; yum-utils

&lt;span class="c"&gt;# Step 2: Add Docker repository&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;yum-config-manager &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--add-repo&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  https://download.docker.com/linux/centos/docker-ce.repo

&lt;span class="c"&gt;# Step 3: Install Docker&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;yum &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  docker-ce &lt;span class="se"&gt;\&lt;/span&gt;
  docker-ce-cli &lt;span class="se"&gt;\&lt;/span&gt;
  containerd.io &lt;span class="se"&gt;\&lt;/span&gt;
  docker-buildx-plugin &lt;span class="se"&gt;\&lt;/span&gt;
  docker-compose-plugin

&lt;span class="c"&gt;# Step 4: Start and enable&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl start docker
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;docker

&lt;span class="c"&gt;# Step 5: Post-install&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;usermod &lt;span class="nt"&gt;-aG&lt;/span&gt; docker &lt;span class="nv"&gt;$USER&lt;/span&gt;
newgrp docker

&lt;span class="c"&gt;# Verify&lt;/span&gt;
docker run hello-world

&lt;span class="c"&gt;# Note: On RHEL 9+, podman is preferred (Docker-compatible CLI)&lt;/span&gt;
&lt;span class="c"&gt;# podman run hello-world  # Same commands, daemonless!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Docker Engine Components — Under the Hood
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Component Stack
&lt;/h3&gt;

&lt;p&gt;The Docker Engine follows this layered stack from top to bottom:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;docker CLI&lt;/strong&gt; — &lt;code&gt;docker run / build / push / pull&lt;/code&gt; (makes REST API calls)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;dockerd — Docker Daemon&lt;/strong&gt; — REST API | &lt;code&gt;/var/run/docker.sock&lt;/code&gt; | Port 2376 (TLS)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;containerd&lt;/strong&gt; — Image management | Container lifecycle | &lt;code&gt;/run/containerd/containerd.sock&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;containerd-shim&lt;/strong&gt; — Daemonless container I/O | stdout/stderr relay&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;runc (OCI Runtime)&lt;/strong&gt; — syscalls to kernel: &lt;code&gt;clone()&lt;/code&gt;, &lt;code&gt;unshare()&lt;/code&gt;, &lt;code&gt;setns()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Linux Kernel&lt;/strong&gt; — namespaces (pid, net, mnt, uts, ipc) | cgroups (cpu, memory, io)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Container Process&lt;/strong&gt; — Your app running as PID 1 inside container&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  dockerd — Docker Daemon Deep Dive
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# View dockerd process&lt;/span&gt;
ps aux | &lt;span class="nb"&gt;grep &lt;/span&gt;dockerd
&lt;span class="c"&gt;# root  1234  ... /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock&lt;/span&gt;

&lt;span class="c"&gt;# Socket files&lt;/span&gt;
&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt; /var/run/docker.sock             &lt;span class="c"&gt;# Unix socket (default)&lt;/span&gt;
&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt; /run/containerd/containerd.sock  &lt;span class="c"&gt;# containerd socket&lt;/span&gt;

&lt;span class="c"&gt;# dockerd config file&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; /etc/docker/daemon.json

&lt;span class="c"&gt;# Restart dockerd (containers will NOT stop!)&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl restart docker

&lt;span class="c"&gt;# View dockerd logs&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;journalctl &lt;span class="nt"&gt;-u&lt;/span&gt; docker.service &lt;span class="nt"&gt;-n&lt;/span&gt; 50 &lt;span class="nt"&gt;--no-pager&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;journalctl &lt;span class="nt"&gt;-u&lt;/span&gt; docker.service &lt;span class="nt"&gt;-f&lt;/span&gt;  &lt;span class="c"&gt;# Live tail&lt;/span&gt;

&lt;span class="c"&gt;# Docker events stream&lt;/span&gt;
docker events  &lt;span class="c"&gt;# Real-time events: container start/stop/die, image pull, etc.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;daemon.json — Production Configuration&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"log-driver"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"json-file"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"log-opts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"max-size"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"100m"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"max-file"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"3"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"default-address-pools"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"base"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"172.17.0.0/12"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"size"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"storage-driver"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"overlay2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"registry-mirrors"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"https://mirror.gcr.io"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"insecure-registries"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"live-restore"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"max-concurrent-downloads"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"max-concurrent-uploads"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"metrics-addr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0.0.0.0:9323"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"experimental"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note (Production Must-Haves):&lt;/strong&gt; &lt;code&gt;log-driver + log-opts&lt;/code&gt;: By default Docker logs can consume unlimited disk. Set &lt;code&gt;max-size: 100m&lt;/code&gt; and &lt;code&gt;max-file: 3&lt;/code&gt; — total 300MB max per container. &lt;code&gt;live-restore: true&lt;/code&gt;: Containers will not stop when the Docker daemon restarts. Critical for zero-downtime daemon updates! &lt;code&gt;storage-driver: overlay2&lt;/code&gt;: Best performance on modern kernels. Also the default on Ubuntu.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  containerd — Detailed
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Inspect containerd directly&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;ctr version
&lt;span class="nb"&gt;sudo &lt;/span&gt;ctr namespaces &lt;span class="nb"&gt;ls&lt;/span&gt;         &lt;span class="c"&gt;# Docker uses 'moby' namespace&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;ctr &lt;span class="nt"&gt;-n&lt;/span&gt; moby images &lt;span class="nb"&gt;ls&lt;/span&gt;     &lt;span class="c"&gt;# Docker images in containerd&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;ctr &lt;span class="nt"&gt;-n&lt;/span&gt; moby containers &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="c"&gt;# Running containers&lt;/span&gt;

&lt;span class="c"&gt;# containerd config&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; /etc/containerd/config.toml

&lt;span class="c"&gt;# Restart containerd (Docker containers will temporarily stop!)&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl restart containerd

&lt;span class="c"&gt;# In Kubernetes clusters (containerd directly)&lt;/span&gt;
&lt;span class="c"&gt;# crictl images         # CRI-compatible CLI&lt;/span&gt;
&lt;span class="c"&gt;# crictl ps             # Running containers&lt;/span&gt;
&lt;span class="c"&gt;# crictl inspect &amp;lt;id&amp;gt;   # Container details&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note (containerd-shim):&lt;/strong&gt; containerd-shim is an intermediate process spawned for each container. Its job: (1) relay stdin/stdout/stderr, (2) report exit status, (3) keep the container alive even if the daemon crashes. This is why &lt;code&gt;live-restore: true&lt;/code&gt; works!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  OCI — Open Container Initiative
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Specification&lt;/th&gt;
&lt;th&gt;Defines&lt;/th&gt;
&lt;th&gt;Implemented By&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Image Spec&lt;/td&gt;
&lt;td&gt;Image format: layers, manifest, config JSON&lt;/td&gt;
&lt;td&gt;Docker, containerd, Buildah&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Runtime Spec&lt;/td&gt;
&lt;td&gt;How to run a container: config.json + rootfs&lt;/td&gt;
&lt;td&gt;runc, crun, kata-containers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Distribution Spec&lt;/td&gt;
&lt;td&gt;How images are pushed/pulled from registries&lt;/td&gt;
&lt;td&gt;Docker Hub, ECR, GCR, Harbor&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip (Interview Question):&lt;/strong&gt; &lt;strong&gt;Q: "What is the difference between Docker and containerd?"&lt;/strong&gt; &lt;strong&gt;A:&lt;/strong&gt; Docker is a complete platform (CLI + daemon + build tools + compose). containerd is just a container runtime — lightweight, CRI-compatible, perfect for Kubernetes. Docker internally uses containerd. Kubernetes bypasses the Docker daemon and talks directly to containerd (since v1.24).&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Understanding Docker Images
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Image — The Blueprint
&lt;/h3&gt;

&lt;p&gt;An image is an immutable blueprint. Think of it this way: a &lt;code&gt;class Component extends React.Component&lt;/code&gt; — you don't directly "run" a class, you create an instance. Similarly, Image = Class, Container = Instance. An image is built once, then thousands of containers can be created from it. The image itself never changes.&lt;/p&gt;

&lt;p&gt;Technically, a Docker image is a read-only filesystem snapshot + metadata (env vars, exposed ports, entry command), organized in layers — each layer representing an incremental change.&lt;/p&gt;

&lt;h3&gt;
  
  
  Image Layers — The Onion Model
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────────────────────────┐
│  Container Layer — WRITABLE             │  ← New files, modifications go here.
│  New files, modifications go here.      │    Deleted when container stops.
│  Deleted when container stops.          │
├─────────────────────────────────────────┤
│           ↑ WRITABLE / READ-ONLY ↓      │
├─────────────────────────────────────────┤
│  Layer 4: COPY . /app                   │  ← Your application source code (~5 MB)
├─────────────────────────────────────────┤
│  Layer 3: RUN npm install               │  ← node_modules directory (~80 MB)
├─────────────────────────────────────────┤
│  Layer 2: WORKDIR /app                  │  ← Creates /app directory (metadata only)
├─────────────────────────────────────────┤
│  Layer 1: FROM node:18-alpine           │  ← Base OS + Node.js runtime (~120 MB,
│                                         │    shared with other node images)
└─────────────────────────────────────────┘
  OverlayFS merges all layers → appears as single filesystem to container
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; OverlayFS is a Linux kernel filesystem driver. It merges multiple read-only layers and one read-write layer into a single unified filesystem. The container sees a normal filesystem, but it is actually a stack of layers. Copy-on-Write is used — when a container modifies a file, it is first copied from the read-only layer to the read-write layer (upperdir), then modified.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Image Manifest and Digest
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# View image manifest (JSON format)&lt;/span&gt;
docker manifest inspect nginx:latest

&lt;span class="c"&gt;# Image digest — immutable SHA256 reference&lt;/span&gt;
docker images &lt;span class="nt"&gt;--digests&lt;/span&gt; nginx
&lt;span class="c"&gt;# REPOSITORY   TAG       DIGEST                                                    IMAGE ID&lt;/span&gt;
&lt;span class="c"&gt;# nginx        latest    sha256:a484819eb60...                                     e784f4560448&lt;/span&gt;

&lt;span class="c"&gt;# Pull by digest (PRODUCTION BEST PRACTICE)&lt;/span&gt;
&lt;span class="c"&gt;# Tags can change (latest is mutable), digests never change!&lt;/span&gt;
docker pull nginx@sha256:a484819eb60211f5299034ac80f6a681b06f89e65866ce91f356ed7c72af059c

&lt;span class="c"&gt;# Tags are mutable — this is dangerous:&lt;/span&gt;
docker pull nginx:latest   &lt;span class="c"&gt;# Today pulls one version, tomorrow may pull another&lt;/span&gt;

&lt;span class="c"&gt;# Safe production pull (pinned digest):&lt;/span&gt;
docker pull nginx@sha256:exact_digest_from_registry

&lt;span class="c"&gt;# Get digest of a local image&lt;/span&gt;
docker inspect nginx &lt;span class="nt"&gt;--format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'{{index .RepoDigests 0}}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; The &lt;code&gt;:latest&lt;/code&gt; tag is a moving target. If you pull today you get 1.25; if CI/CD pulls tomorrow it might get 1.26 — with breaking changes. In production, always pin semantic versions: &lt;code&gt;nginx:1.25.3-alpine&lt;/code&gt; or use digests. &lt;code&gt;:latest&lt;/code&gt; is only acceptable for local development.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Image Storage on Disk
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Where Docker images are stored on disk&lt;/span&gt;
&lt;span class="nb"&gt;ls&lt;/span&gt; /var/lib/docker/

&lt;span class="c"&gt;# Image layers (overlay2 storage driver)&lt;/span&gt;
&lt;span class="nb"&gt;sudo ls&lt;/span&gt; /var/lib/docker/overlay2/
&lt;span class="c"&gt;# Each directory is a layer&lt;/span&gt;

&lt;span class="c"&gt;# Image metadata&lt;/span&gt;
&lt;span class="nb"&gt;sudo ls&lt;/span&gt; /var/lib/docker/image/overlay2/imagedb/content/sha256/
&lt;span class="c"&gt;# Each file is an image manifest/config&lt;/span&gt;

&lt;span class="c"&gt;# Disk usage breakdown — important for DevOps!&lt;/span&gt;
docker system &lt;span class="nb"&gt;df&lt;/span&gt;
&lt;span class="c"&gt;# TYPE            TOTAL     ACTIVE    SIZE      RECLAIMABLE&lt;/span&gt;
&lt;span class="c"&gt;# Images          15        3         2.1GB     1.8GB (85%)&lt;/span&gt;
&lt;span class="c"&gt;# Containers      5         2         45MB      23MB (51%)&lt;/span&gt;
&lt;span class="c"&gt;# Local Volumes   8         3         560MB     300MB (53%)&lt;/span&gt;
&lt;span class="c"&gt;# Build Cache     0         0         0B        0B&lt;/span&gt;

&lt;span class="c"&gt;# Detailed breakdown&lt;/span&gt;
docker system &lt;span class="nb"&gt;df&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt;

&lt;span class="c"&gt;# View layer sharing (images share base layers)&lt;/span&gt;
docker &lt;span class="nb"&gt;history &lt;/span&gt;nginx:latest &lt;span class="nt"&gt;--no-trunc&lt;/span&gt;
docker &lt;span class="nb"&gt;history &lt;/span&gt;nginx:alpine &lt;span class="nt"&gt;--no-trunc&lt;/span&gt;
&lt;span class="c"&gt;# Common layers are shared — only one copy on disk!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Image Tags vs Digests vs IDs
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concept&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;th&gt;Mutable?&lt;/th&gt;
&lt;th&gt;Use Case&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Image ID&lt;/td&gt;
&lt;td&gt;&lt;code&gt;e784f4560448&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No (content-addressed)&lt;/td&gt;
&lt;td&gt;Local reference, scripting&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tag&lt;/td&gt;
&lt;td&gt;&lt;code&gt;nginx:1.25.3&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Yes (can be moved)&lt;/td&gt;
&lt;td&gt;Human-readable, dev/staging&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Digest&lt;/td&gt;
&lt;td&gt;&lt;code&gt;nginx@sha256:abc...&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No (cryptographic)&lt;/td&gt;
&lt;td&gt;Production deployments, security&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;latest tag&lt;/td&gt;
&lt;td&gt;&lt;code&gt;nginx:latest&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Yes (frequently moves)&lt;/td&gt;
&lt;td&gt;Local dev ONLY, never production&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip (Interview Question):&lt;/strong&gt; &lt;strong&gt;Q: "What is the difference between a Docker image and a container?"&lt;/strong&gt; &lt;strong&gt;A:&lt;/strong&gt; An image is a read-only blueprint/template — a static filesystem snapshot. A container is a running instance of that image, with a writable layer added on top. You can create thousands of containers from one image. When a container is stopped, the writable layer is deleted (unless committed or a volume is used).&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Working with Images — Command Deep Dive
&lt;/h2&gt;

&lt;h3&gt;
  
  
  docker pull — Downloading Images
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Purpose:&lt;/strong&gt; Download images from a registry to your local machine. Pull without &lt;code&gt;docker run&lt;/code&gt; when you want the image without starting a container.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Basic pull — pulls latest tag (avoid in production!)&lt;/span&gt;
docker pull nginx

&lt;span class="c"&gt;# Specific tag pull (recommended)&lt;/span&gt;
docker pull nginx:1.25.3-alpine

&lt;span class="c"&gt;# Specific digest pull (MOST RECOMMENDED for production)&lt;/span&gt;
docker pull nginx@sha256:a484819eb60211f5299034ac80f6a681b06f89e65866ce91f356ed7c72af059c

&lt;span class="c"&gt;# Pull from private registry&lt;/span&gt;
docker pull myregistry.company.com/myapp:v1.2.3

&lt;span class="c"&gt;# Pull from AWS ECR (login first)&lt;/span&gt;
aws ecr get-login-password &lt;span class="nt"&gt;--region&lt;/span&gt; ap-south-1 | &lt;span class="se"&gt;\&lt;/span&gt;
  docker login &lt;span class="nt"&gt;--username&lt;/span&gt; AWS &lt;span class="nt"&gt;--password-stdin&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  123456789012.dkr.ecr.ap-south-1.amazonaws.com

docker pull 123456789012.dkr.ecr.ap-south-1.amazonaws.com/myapp:latest

&lt;span class="c"&gt;# Pull from GCR&lt;/span&gt;
docker pull gcr.io/my-project/myapp:v2.0.0

&lt;span class="c"&gt;# Pull from GitHub Container Registry&lt;/span&gt;
docker pull ghcr.io/myorg/myapp:main

&lt;span class="c"&gt;# Specify platform (ARM/AMD64)&lt;/span&gt;
docker pull &lt;span class="nt"&gt;--platform&lt;/span&gt; linux/amd64 nginx:latest
docker pull &lt;span class="nt"&gt;--platform&lt;/span&gt; linux/arm64 nginx:latest  &lt;span class="c"&gt;# For Apple Silicon&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; Always specify a tag when pulling. &lt;code&gt;docker pull nginx&lt;/code&gt; = &lt;code&gt;docker pull nginx:latest&lt;/code&gt;. If the &lt;code&gt;latest&lt;/code&gt; tag is updated tomorrow, your CI/CD will pull a different image. Fix: &lt;code&gt;docker pull nginx:1.25.3&lt;/code&gt; or use a digest.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  docker images — Listing Images
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Basic list&lt;/span&gt;
docker images
docker image &lt;span class="nb"&gt;ls&lt;/span&gt;  &lt;span class="c"&gt;# Same command, new syntax&lt;/span&gt;

&lt;span class="c"&gt;# Table format with size&lt;/span&gt;
docker images &lt;span class="nt"&gt;--format&lt;/span&gt; &lt;span class="s2"&gt;"table {{.Repository}}&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s2"&gt;{{.Tag}}&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s2"&gt;{{.Size}}&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s2"&gt;{{.CreatedAt}}"&lt;/span&gt;

&lt;span class="c"&gt;# Filter dangling images (untagged, build artifacts)&lt;/span&gt;
docker images &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="nv"&gt;dangling&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;

&lt;span class="c"&gt;# Filter by label&lt;/span&gt;
docker images &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="nv"&gt;label&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;env&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;production

&lt;span class="c"&gt;# Image IDs only (useful for scripting)&lt;/span&gt;
docker images &lt;span class="nt"&gt;-q&lt;/span&gt;

&lt;span class="c"&gt;# Show images for a specific repository&lt;/span&gt;
docker images nginx

&lt;span class="c"&gt;# Custom format&lt;/span&gt;
docker images &lt;span class="nt"&gt;--format&lt;/span&gt; &lt;span class="s2"&gt;"{{.ID}}: {{.Repository}}:{{.Tag}} — {{.Size}}"&lt;/span&gt;

&lt;span class="c"&gt;# All images (including intermediates)&lt;/span&gt;
docker images &lt;span class="nt"&gt;-a&lt;/span&gt;

&lt;span class="c"&gt;# Images with digests&lt;/span&gt;
docker images &lt;span class="nt"&gt;--digests&lt;/span&gt;

&lt;span class="c"&gt;# Sort by size&lt;/span&gt;
docker images &lt;span class="nt"&gt;--format&lt;/span&gt; &lt;span class="s2"&gt;"{{.Size}}&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s2"&gt;{{.Repository}}:{{.Tag}}"&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-h&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  docker rmi — Deleting Images
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Remove by name&lt;/span&gt;
docker rmi nginx
docker image &lt;span class="nb"&gt;rm &lt;/span&gt;nginx

&lt;span class="c"&gt;# Specify tag (when multiple tags exist)&lt;/span&gt;
docker rmi nginx:1.25.3-alpine

&lt;span class="c"&gt;# Remove by image ID&lt;/span&gt;
docker rmi e784f4560448

&lt;span class="c"&gt;# Remove multiple images at once&lt;/span&gt;
docker rmi nginx:latest ubuntu:20.04 redis:alpine

&lt;span class="c"&gt;# Force remove — even if a container is using it (dangerous!)&lt;/span&gt;
docker rmi &lt;span class="nt"&gt;-f&lt;/span&gt; nginx:latest

&lt;span class="c"&gt;# Remove dangling images (safe cleanup)&lt;/span&gt;
docker image prune

&lt;span class="c"&gt;# Remove all unused images (aggressive cleanup)&lt;/span&gt;
docker image prune &lt;span class="nt"&gt;-a&lt;/span&gt;

&lt;span class="c"&gt;# Remove without confirmation&lt;/span&gt;
docker image prune &lt;span class="nt"&gt;-f&lt;/span&gt;
docker image prune &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt;

&lt;span class="c"&gt;# Nuclear option — delete all images (careful!)&lt;/span&gt;
docker rmi &lt;span class="si"&gt;$(&lt;/span&gt;docker images &lt;span class="nt"&gt;-q&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Delete by age filter&lt;/span&gt;
docker image prune &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nt"&gt;--filter&lt;/span&gt; &lt;span class="s2"&gt;"until=24h"&lt;/span&gt;   &lt;span class="c"&gt;# Older than 24 hours&lt;/span&gt;
docker image prune &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nt"&gt;--filter&lt;/span&gt; &lt;span class="s2"&gt;"until=168h"&lt;/span&gt;  &lt;span class="c"&gt;# Older than 7 days&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; An image used by a running container cannot be deleted — &lt;code&gt;docker rmi&lt;/code&gt; will fail. Stop and remove the container first: &lt;code&gt;docker stop &amp;lt;container&amp;gt; &amp;amp;&amp;amp; docker rm &amp;lt;container&amp;gt;&lt;/code&gt;, then &lt;code&gt;docker rmi &amp;lt;image&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  docker tag — Tagging Images
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Tag for Docker Hub&lt;/span&gt;
docker tag myapp:latest myusername/myapp:v1.0.0
docker tag myapp:latest myusername/myapp:latest

&lt;span class="c"&gt;# Tag for AWS ECR&lt;/span&gt;
docker tag myapp:latest &lt;span class="se"&gt;\&lt;/span&gt;
  123456789012.dkr.ecr.ap-south-1.amazonaws.com/myapp:v1.0.0

&lt;span class="c"&gt;# Tag for GCR&lt;/span&gt;
docker tag myapp:latest gcr.io/my-gcp-project/myapp:v1.0.0

&lt;span class="c"&gt;# Tag for GitHub Container Registry&lt;/span&gt;
docker tag myapp:latest ghcr.io/myorg/myapp:v1.0.0
docker tag myapp:latest ghcr.io/myorg/myapp:main-abc1234  &lt;span class="c"&gt;# Git commit hash&lt;/span&gt;

&lt;span class="c"&gt;# CI/CD common pattern (git hash + version)&lt;/span&gt;
&lt;span class="nv"&gt;GIT_HASH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git rev-parse &lt;span class="nt"&gt;--short&lt;/span&gt; HEAD&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"1.2.3"&lt;/span&gt;
docker tag myapp:latest myregistry.io/myapp:&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;-&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;GIT_HASH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
docker tag myapp:latest myregistry.io/myapp:&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
docker tag myapp:latest myregistry.io/myapp:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip (Tagging Strategy for CI/CD):&lt;/strong&gt; Good tagging strategy: (1) Git commit hash tag — always, immutable and traceable. (2) Semantic version tag. (3) Branch name tag (e.g., &lt;code&gt;main&lt;/code&gt;, &lt;code&gt;develop&lt;/code&gt;). (4) &lt;code&gt;latest&lt;/code&gt; only on main/master branch. This strategy makes both rollback and debugging easy.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  docker save &amp;amp; docker load — Transferring Images
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Save a single image&lt;/span&gt;
docker save &lt;span class="nt"&gt;-o&lt;/span&gt; myapp.tar myapp:latest

&lt;span class="c"&gt;# Save multiple images into one tar&lt;/span&gt;
docker save &lt;span class="nt"&gt;-o&lt;/span&gt; all-images.tar nginx:latest redis:alpine postgres:15

&lt;span class="c"&gt;# Compressed save (saves disk space)&lt;/span&gt;
docker save myapp:latest | &lt;span class="nb"&gt;gzip&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; myapp.tar.gz

&lt;span class="c"&gt;# Load from tar file&lt;/span&gt;
docker load &lt;span class="nt"&gt;-i&lt;/span&gt; myapp.tar

&lt;span class="c"&gt;# Load compressed&lt;/span&gt;
&lt;span class="nb"&gt;gunzip&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; myapp.tar.gz | docker load

&lt;span class="c"&gt;# Transfer via SSH (no file on disk!)&lt;/span&gt;
docker save myapp:latest | ssh user@remote-server docker load

&lt;span class="c"&gt;# Verify loaded image&lt;/span&gt;
docker images | &lt;span class="nb"&gt;grep &lt;/span&gt;myapp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  docker history — Viewing Image Layers
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# View image history&lt;/span&gt;
docker &lt;span class="nb"&gt;history &lt;/span&gt;nginx

&lt;span class="c"&gt;# Full commands (not truncated)&lt;/span&gt;
docker &lt;span class="nb"&gt;history&lt;/span&gt; &lt;span class="nt"&gt;--no-trunc&lt;/span&gt; nginx

&lt;span class="c"&gt;# Custom format&lt;/span&gt;
docker &lt;span class="nb"&gt;history&lt;/span&gt; &lt;span class="nt"&gt;--format&lt;/span&gt; &lt;span class="s2"&gt;"table {{.ID}}&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s2"&gt;{{.Size}}&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s2"&gt;{{.CreatedBy}}"&lt;/span&gt; nginx

&lt;span class="c"&gt;# Identify large layers&lt;/span&gt;
docker &lt;span class="nb"&gt;history &lt;/span&gt;nginx &lt;span class="nt"&gt;--format&lt;/span&gt; &lt;span class="s2"&gt;"{{.Size}}&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s2"&gt;{{.CreatedBy}}"&lt;/span&gt; | &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="s2"&gt;"^0B"&lt;/span&gt; | &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-rh&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  docker inspect — Deep Inspection
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Full JSON inspection&lt;/span&gt;
docker inspect nginx
docker inspect nginx | jq  &lt;span class="c"&gt;# Pretty printed (install jq: sudo apt install jq)&lt;/span&gt;

&lt;span class="c"&gt;# Extract specific fields (Go template syntax)&lt;/span&gt;
docker inspect &lt;span class="nt"&gt;--format&lt;/span&gt; &lt;span class="s1"&gt;'{{.Config.Env}}'&lt;/span&gt; nginx
docker inspect &lt;span class="nt"&gt;--format&lt;/span&gt; &lt;span class="s1"&gt;'{{.Config.ExposedPorts}}'&lt;/span&gt; nginx
docker inspect &lt;span class="nt"&gt;--format&lt;/span&gt; &lt;span class="s1"&gt;'{{.Config.Cmd}}'&lt;/span&gt; nginx

&lt;span class="c"&gt;# For containers: get IP address&lt;/span&gt;
docker inspect &lt;span class="nt"&gt;--format&lt;/span&gt; &lt;span class="s1"&gt;'{{.NetworkSettings.IPAddress}}'&lt;/span&gt; my_container

&lt;span class="c"&gt;# Container mount points&lt;/span&gt;
docker inspect &lt;span class="nt"&gt;--format&lt;/span&gt; &lt;span class="s1"&gt;'{{json .Mounts}}'&lt;/span&gt; my_container | jq

&lt;span class="c"&gt;# Container restart policy&lt;/span&gt;
docker inspect &lt;span class="nt"&gt;--format&lt;/span&gt; &lt;span class="s1"&gt;'{{.HostConfig.RestartPolicy}}'&lt;/span&gt; my_container

&lt;span class="c"&gt;# Image layers (layer hashes)&lt;/span&gt;
docker inspect nginx | jq &lt;span class="s1"&gt;'.[0].RootFS.Layers'&lt;/span&gt;

&lt;span class="c"&gt;# Container environment variables&lt;/span&gt;
docker inspect my_container | jq &lt;span class="s1"&gt;'.[0].Config.Env[]'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  docker push — Uploading to Registry
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Login first&lt;/span&gt;
docker login                               &lt;span class="c"&gt;# Docker Hub&lt;/span&gt;
docker login myregistry.company.com        &lt;span class="c"&gt;# Private registry&lt;/span&gt;

&lt;span class="c"&gt;# AWS ECR login&lt;/span&gt;
aws ecr get-login-password &lt;span class="nt"&gt;--region&lt;/span&gt; ap-south-1 | &lt;span class="se"&gt;\&lt;/span&gt;
  docker login &lt;span class="nt"&gt;--username&lt;/span&gt; AWS &lt;span class="nt"&gt;--password-stdin&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  123456789012.dkr.ecr.ap-south-1.amazonaws.com

&lt;span class="c"&gt;# GitHub Container Registry login&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$GITHUB_TOKEN&lt;/span&gt; | docker login ghcr.io &lt;span class="nt"&gt;-u&lt;/span&gt; &lt;span class="nv"&gt;$GITHUB_USERNAME&lt;/span&gt; &lt;span class="nt"&gt;--password-stdin&lt;/span&gt;

&lt;span class="c"&gt;# Push&lt;/span&gt;
docker push myusername/myapp:v1.0.0
docker push myusername/myapp:latest

&lt;span class="c"&gt;# Push multiple tags (loop)&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;TAG &lt;span class="k"&gt;in &lt;/span&gt;v1.0.0 v1.0 v1 latest&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;docker push myregistry.io/myapp:&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TAG&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;

&lt;span class="c"&gt;# Logout (for security on shared machines)&lt;/span&gt;
docker &lt;span class="nb"&gt;logout
&lt;/span&gt;docker &lt;span class="nb"&gt;logout &lt;/span&gt;myregistry.company.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Image Layers Deep Dive — Caching &amp;amp; Optimization
&lt;/h2&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  &lt;/p&gt;
Place stable layers (base image, dependencies) below volatile ones (source code) so Docker's layer cache is reused on every incremental build.





&lt;h3&gt;
  
  
  OverlayFS — Technical Deep Dive
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;OverlayFS Component&lt;/th&gt;
&lt;th&gt;Docker Equivalent&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;lowerdir&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Image layers (read-only)&lt;/td&gt;
&lt;td&gt;Base image + all intermediate layers — never modified&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;upperdir&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Container's writable layer&lt;/td&gt;
&lt;td&gt;Container-specific changes go here&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;workdir&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Internal OverlayFS use&lt;/td&gt;
&lt;td&gt;Staging area for atomic operations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;merged&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Container's filesystem view&lt;/td&gt;
&lt;td&gt;Unified mount — what the container sees&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# View OverlayFS mount for a running container&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; mynginx nginx

&lt;span class="c"&gt;# Get mount info&lt;/span&gt;
docker inspect mynginx | jq &lt;span class="s1"&gt;'.[0].GraphDriver'&lt;/span&gt;
&lt;span class="c"&gt;# {&lt;/span&gt;
&lt;span class="c"&gt;#   "Data": {&lt;/span&gt;
&lt;span class="c"&gt;#     "LowerDir": "/var/lib/docker/overlay2/abc.../diff:/var/lib/docker/overlay2/def.../diff",&lt;/span&gt;
&lt;span class="c"&gt;#     "MergedDir": "/var/lib/docker/overlay2/xyz.../merged",&lt;/span&gt;
&lt;span class="c"&gt;#     "UpperDir": "/var/lib/docker/overlay2/xyz.../diff",&lt;/span&gt;
&lt;span class="c"&gt;#     "WorkDir": "/var/lib/docker/overlay2/xyz.../work"&lt;/span&gt;
&lt;span class="c"&gt;#   },&lt;/span&gt;
&lt;span class="c"&gt;#   "Name": "overlay2"&lt;/span&gt;
&lt;span class="c"&gt;# }&lt;/span&gt;

&lt;span class="c"&gt;# View actual filesystem layering on host&lt;/span&gt;
&lt;span class="nb"&gt;sudo ls&lt;/span&gt; /var/lib/docker/overlay2/

&lt;span class="c"&gt;# View mount point on host&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; /proc/mounts | &lt;span class="nb"&gt;grep &lt;/span&gt;overlay

&lt;span class="c"&gt;# Demonstrate Copy-on-Write&lt;/span&gt;
docker &lt;span class="nb"&gt;exec &lt;/span&gt;mynginx sh &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"echo 'hello' &amp;gt; /usr/share/nginx/html/test.html"&lt;/span&gt;
&lt;span class="c"&gt;# The file went into the container's upperdir, NOT into the image.&lt;/span&gt;
&lt;span class="c"&gt;# The image remains unchanged.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Layer Caching — The Secret of Build Speed
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# First build — everything downloaded and executed (slow)&lt;/span&gt;
docker build &lt;span class="nt"&gt;-t&lt;/span&gt; myapp:v1 &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="c"&gt;# Step 1/5 : FROM node:18-alpine      → DOWNLOADING (slow)&lt;/span&gt;
&lt;span class="c"&gt;# Step 2/5 : WORKDIR /app             → Running&lt;/span&gt;
&lt;span class="c"&gt;# Step 3/5 : COPY package*.json ./    → Running&lt;/span&gt;
&lt;span class="c"&gt;# Step 4/5 : RUN npm install          → Installing (slow!)&lt;/span&gt;
&lt;span class="c"&gt;# Step 5/5 : COPY . .                 → Running&lt;/span&gt;

&lt;span class="c"&gt;# Second build (only source code changed)&lt;/span&gt;
docker build &lt;span class="nt"&gt;-t&lt;/span&gt; myapp:v2 &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="c"&gt;# Step 1/5 : FROM node:18-alpine      → Using cache  ✓ (instant!)&lt;/span&gt;
&lt;span class="c"&gt;# Step 2/5 : WORKDIR /app             → Using cache  ✓ (instant!)&lt;/span&gt;
&lt;span class="c"&gt;# Step 3/5 : COPY package*.json ./    → Using cache  ✓ (package.json unchanged)&lt;/span&gt;
&lt;span class="c"&gt;# Step 4/5 : RUN npm install          → Using cache  ✓ (instant!)&lt;/span&gt;
&lt;span class="c"&gt;# Step 5/5 : COPY . .                 → Running      ← Only this re-runs!&lt;/span&gt;
&lt;span class="c"&gt;# Total time: 2 seconds vs 3 minutes!&lt;/span&gt;

&lt;span class="c"&gt;# Force bypass cache&lt;/span&gt;
docker build &lt;span class="nt"&gt;--no-cache&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; myapp:fresh &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="c"&gt;# Use cache from external source (CI/CD optimization)&lt;/span&gt;
docker build &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--cache-from&lt;/span&gt; myregistry.io/myapp:latest &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-t&lt;/span&gt; myapp:new &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Cache Invalidation — When Cache Breaks
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Instruction&lt;/th&gt;
&lt;th&gt;Cache Invalidates When&lt;/th&gt;
&lt;th&gt;Note&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;FROM&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Base image digest changes&lt;/td&gt;
&lt;td&gt;Rarely — unless you explicitly pull new base&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RUN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Command text changes&lt;/td&gt;
&lt;td&gt;Even whitespace change = cache miss!&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;COPY / ADD&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;File content changes (checksum)&lt;/td&gt;
&lt;td&gt;Even 1 byte change = cache miss for this layer + all subsequent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ENV&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Value changes&lt;/td&gt;
&lt;td&gt;All subsequent layers also invalidated&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ARG&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Build arg value changes&lt;/td&gt;
&lt;td&gt;Be careful with VERSION args — they invalidate builds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;WORKDIR&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Path changes&lt;/td&gt;
&lt;td&gt;Usually stable&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; A cache miss in one layer causes ALL subsequent layers to re-run. This is why layer ordering is so important — put frequently changing instructions last, stable instructions first.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Layer Ordering — Cache Optimization
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# ❌ BAD — npm install runs EVERY TIME any file changes&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:18-alpine&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .           # Source code AND package.json copied together&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;    &lt;span class="c"&gt;# Re-runs even if only App.jsx changed!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# ✅ GOOD — npm install only when package.json changes&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:18-alpine&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="c"&gt;# Step 1: Copy ONLY package files first&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package.json package-lock.json ./&lt;/span&gt;

&lt;span class="c"&gt;# Step 2: Install dependencies (CACHED as long as package.json is unchanged)&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;

&lt;span class="c"&gt;# Step 3: Now copy source code (this layer changes with source changes)&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;

&lt;span class="c"&gt;# Build&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm run build

&lt;span class="c"&gt;# Serve&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["node", "server.js"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# ✅ BEST — Python/pip example follows the same principle&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; python:3.11-slim&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="c"&gt;# Dependencies first (stable layer)&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; requirements.txt .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--no-cache-dir&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt

&lt;span class="c"&gt;# Source code later (frequently changing)&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["python", "app.py"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# ✅ BEST — Java/Maven example&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;maven:3.9-eclipse-temurin-17&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;build&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="c"&gt;# Copy pom.xml first (dependencies are stable)&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; pom.xml .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;mvn dependency:go-offline &lt;span class="nt"&gt;-q&lt;/span&gt;  &lt;span class="c"&gt;# Download all dependencies (cached!)&lt;/span&gt;

&lt;span class="c"&gt;# Source code later&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; src ./src&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;mvn package &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="nt"&gt;-DskipTests&lt;/span&gt;

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; eclipse-temurin:17-jre-alpine&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=build /app/target/*.jar app.jar&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["java", "-jar", "app.jar"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note (Golden Rule of Layer Ordering):&lt;/strong&gt; Think of it this way: "What changes least goes first." Order of stability: Base image &amp;gt; System packages &amp;gt; Dependency files &amp;gt; Dependencies install &amp;gt; Config files &amp;gt; Source code &amp;gt; Build artifacts. Following this order will dramatically speed up CI/CD builds.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Layer Sharing — Memory &amp;amp; Disk Efficiency
&lt;/h3&gt;

&lt;p&gt;When 50 Node.js microservices all use the same &lt;code&gt;node:18-alpine&lt;/code&gt; base image:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Without sharing: 50 × 120MB = 6 GB&lt;/li&gt;
&lt;li&gt;With Docker layer sharing: 120MB (base, stored once) + 50 × app_size&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This means more containers fit per Kubernetes node, node startup is faster (layer already cached), and network bandwidth is saved during pulls.&lt;/p&gt;

&lt;h3&gt;
  
  
  Layer Analysis Tools
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Method 1: docker history (built-in)&lt;/span&gt;
docker &lt;span class="nb"&gt;history&lt;/span&gt; &lt;span class="nt"&gt;--no-trunc&lt;/span&gt; myapp:latest

&lt;span class="c"&gt;# Method 2: docker inspect layers&lt;/span&gt;
docker inspect myapp:latest | jq &lt;span class="s1"&gt;'.[0].RootFS.Layers'&lt;/span&gt;

&lt;span class="c"&gt;# Method 3: dive tool (BEST for optimization)&lt;/span&gt;
&lt;span class="c"&gt;# Installation&lt;/span&gt;
wget https://github.com/wagoodman/dive/releases/download/v0.12.0/dive_0.12.0_linux_amd64.deb
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; ./dive_0.12.0_linux_amd64.deb

&lt;span class="c"&gt;# Interactive layer analysis&lt;/span&gt;
dive myapp:latest
&lt;span class="c"&gt;# Left panel: layer list + size&lt;/span&gt;
&lt;span class="c"&gt;# Right panel: filesystem changes per layer&lt;/span&gt;
&lt;span class="c"&gt;# Shows wasted space — e.g., files deleted in later layers&lt;/span&gt;
&lt;span class="c"&gt;# Gives an efficiency score&lt;/span&gt;

&lt;span class="c"&gt;# CI mode (automated check)&lt;/span&gt;
dive myapp:latest &lt;span class="nt"&gt;--ci&lt;/span&gt;   &lt;span class="c"&gt;# Fails if efficiency is below threshold&lt;/span&gt;

&lt;span class="c"&gt;# Method 4: BuildKit inline --progress=plain&lt;/span&gt;
&lt;span class="nv"&gt;DOCKER_BUILDKIT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 docker build &lt;span class="nt"&gt;--progress&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;plain &lt;span class="nt"&gt;-t&lt;/span&gt; myapp:latest &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="c"&gt;# Shows each step's timing and cache status&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Advanced Layer Optimization Techniques
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Technique 1: Combine RUN commands (fewer layers)&lt;/span&gt;
&lt;span class="c"&gt;# ❌ BAD — 3 layers for apt operations&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apt update
&lt;span class="k"&gt;RUN &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; curl wget
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /var/lib/apt/lists/&lt;span class="k"&gt;*&lt;/span&gt;

&lt;span class="c"&gt;# ✅ GOOD — Single layer, cleanup in SAME layer&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; curl wget &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /var/lib/apt/lists/&lt;span class="k"&gt;*&lt;/span&gt;
&lt;span class="c"&gt;# IMPORTANT: Cleanup must be in the SAME RUN instruction!&lt;/span&gt;
&lt;span class="c"&gt;# Cleaning in a separate RUN does NOT reduce layer size&lt;/span&gt;
&lt;span class="c"&gt;# (the layer with apt cache already exists)&lt;/span&gt;

&lt;span class="c"&gt;# Technique 2: Multi-stage builds (drastically reduce SIZE)&lt;/span&gt;
&lt;span class="c"&gt;# Build stage&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;node:18&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;builder&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm ci &lt;span class="nt"&gt;--production&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;  &lt;span class="c"&gt;# All deps including devDependencies&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm run build               &lt;span class="c"&gt;# Create production build&lt;/span&gt;

&lt;span class="c"&gt;# Production stage (runtime only!)&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:18-alpine             # Slim base (50MB vs 1GB!)&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /app/dist ./dist&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /app/node_modules ./node_modules&lt;/span&gt;
&lt;span class="c"&gt;# Build tools, devDeps, source code do not go into the production image!&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 3000&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["node", "dist/server.js"]&lt;/span&gt;

&lt;span class="c"&gt;# Result: 1.2GB → 180MB image!&lt;/span&gt;

&lt;span class="c"&gt;# Technique 3: .dockerignore (like .gitignore for Docker)&lt;/span&gt;
&lt;span class="c"&gt;# node_modules/      ← exclude from COPY&lt;/span&gt;
&lt;span class="c"&gt;# .git/              ← unnecessary&lt;/span&gt;
&lt;span class="c"&gt;# dist/              ← build output (will be rebuilt)&lt;/span&gt;
&lt;span class="c"&gt;# *.log              ← log files&lt;/span&gt;
&lt;span class="c"&gt;# .env               ← Secrets! NEVER in image&lt;/span&gt;
&lt;span class="c"&gt;# coverage/          ← test coverage reports&lt;/span&gt;

&lt;span class="c"&gt;# Technique 4: Specific COPY (avoid COPY . .)&lt;/span&gt;
&lt;span class="c"&gt;# ❌ BAD&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .              # Copies everything including .git, tests, docs&lt;/span&gt;

&lt;span class="c"&gt;# ✅ GOOD&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; src/ ./src/&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; public/ ./public/&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip (Senior Interview Question):&lt;/strong&gt; &lt;strong&gt;Q: "A Docker image has grown to 2GB — how would you optimize it?"&lt;/strong&gt; &lt;strong&gt;A:&lt;/strong&gt; (1) Use &lt;code&gt;docker history --no-trunc myimage&lt;/code&gt; to identify large layers. (2) Use multi-stage builds — don't include build tools in production. (3) Use Alpine base image (50MB vs 900MB). (4) Run &lt;code&gt;apt-get clean &amp;amp;&amp;amp; rm -rf /var/lib/apt/lists/*&lt;/code&gt; in the same RUN instruction. (5) Use &lt;code&gt;.dockerignore&lt;/code&gt; to exclude unnecessary files. (6) Use the &lt;code&gt;dive&lt;/code&gt; tool to find wasted space. (7) Combine RUN instructions. Typically, 2GB → 150-200MB is achievable!&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Container Lifecycle
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Container States
&lt;/h3&gt;

&lt;p&gt;Every Docker container passes through multiple states during its life. Think of them like React component lifecycle (mount, update, unmount) — but more complex:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CREATED&lt;/strong&gt; — Container created via &lt;code&gt;docker create&lt;/code&gt; but not started&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RUNNING&lt;/strong&gt; — Container is active&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PAUSED&lt;/strong&gt; — Frozen, consuming no CPU&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;EXITED&lt;/strong&gt; — Stopped or done (via &lt;code&gt;docker stop&lt;/code&gt;, &lt;code&gt;docker kill&lt;/code&gt;, exit, or crash)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RESTARTING&lt;/strong&gt; — Auto-restart loop in progress&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DEAD&lt;/strong&gt; — Restart attempts failed, max retries exceeded&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;REMOVED&lt;/strong&gt; — Container permanently deleted via &lt;code&gt;docker rm&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Image vs Container
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Image&lt;/th&gt;
&lt;th&gt;Container&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Nature&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Blueprint / Template&lt;/td&gt;
&lt;td&gt;Running instance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;State&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Immutable, static (never changes)&lt;/td&gt;
&lt;td&gt;Mutable, stateful (can change)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Storage&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Read-only layers&lt;/td&gt;
&lt;td&gt;Image layers + writable layer on top&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Multiple instances&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Shared by all containers using it&lt;/td&gt;
&lt;td&gt;Each container fully independent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Lifecycle&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Build once, use forever&lt;/td&gt;
&lt;td&gt;Create → Run → Stop → Delete&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Disk space&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Stored once even if 100 containers use it&lt;/td&gt;
&lt;td&gt;Only writable layer extra per container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Analogy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Recipe&lt;/td&gt;
&lt;td&gt;The actual dish&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;React analogy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;React Component class/function&lt;/td&gt;
&lt;td&gt;Component instance rendered in DOM&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Container Lifecycle Commands — Quick Reference
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Full lifecycle — a container's complete journey&lt;/span&gt;
docker create &lt;span class="nt"&gt;--name&lt;/span&gt; myapp nginx          &lt;span class="c"&gt;# Create but don't start&lt;/span&gt;
docker start myapp                        &lt;span class="c"&gt;# Start it&lt;/span&gt;
docker pause myapp                        &lt;span class="c"&gt;# Freeze it (stops CPU usage)&lt;/span&gt;
docker unpause myapp                      &lt;span class="c"&gt;# Unfreeze it&lt;/span&gt;
docker stop myapp                         &lt;span class="c"&gt;# Graceful stop (SIGTERM then SIGKILL)&lt;/span&gt;
docker start myapp                        &lt;span class="c"&gt;# Start again&lt;/span&gt;
docker restart myapp                      &lt;span class="c"&gt;# Stop + Start in one command&lt;/span&gt;
docker &lt;span class="nb"&gt;kill &lt;/span&gt;myapp                         &lt;span class="c"&gt;# Immediate kill (SIGKILL)&lt;/span&gt;
docker &lt;span class="nb"&gt;rm &lt;/span&gt;myapp                           &lt;span class="c"&gt;# Remove container (permanently delete)&lt;/span&gt;

&lt;span class="c"&gt;# One-liner (create + start + remove when done — for temporary tasks)&lt;/span&gt;
docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; temp-app nginx

&lt;span class="c"&gt;# Check current state of all containers&lt;/span&gt;
docker ps &lt;span class="nt"&gt;-a&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; &lt;code&gt;docker run&lt;/code&gt; = &lt;code&gt;docker create&lt;/code&gt; + &lt;code&gt;docker start&lt;/code&gt; + &lt;code&gt;docker attach&lt;/code&gt; (in foreground mode). CI/CD pipelines almost always use &lt;code&gt;docker run&lt;/code&gt;. &lt;code&gt;docker create&lt;/code&gt; is useful when you want to set up a container first and start it later.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; If a container enters the DEAD state, it cannot be restarted. You can only remove it with &lt;code&gt;docker rm&lt;/code&gt;. This happens when restart policy max retries are exhausted. Monitor DEAD containers in production!&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Essential Container Commands
&lt;/h2&gt;

&lt;h3&gt;
  
  
  docker run — The Master Command
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;docker run&lt;/code&gt; creates a container, starts it, and optionally attaches to it. Understanding its options is essential.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Full syntax&lt;/span&gt;
docker run &lt;span class="o"&gt;[&lt;/span&gt;OPTIONS] IMAGE &lt;span class="o"&gt;[&lt;/span&gt;COMMAND] &lt;span class="o"&gt;[&lt;/span&gt;ARG...]

&lt;span class="c"&gt;# -d: Detached mode (run in background)&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; nginx
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; my-nginx nginx

&lt;span class="c"&gt;# -p: Port mapping (host_port:container_port)&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:80 nginx             &lt;span class="c"&gt;# Host 8080 → Container 80&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 127.0.0.1:8080:80 nginx  &lt;span class="c"&gt;# Bind to specific IP&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:80 &lt;span class="nt"&gt;-p&lt;/span&gt; 443:443 nginx &lt;span class="c"&gt;# Multiple ports&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-P&lt;/span&gt; nginx                     &lt;span class="c"&gt;# Auto-map ALL EXPOSED ports&lt;/span&gt;

&lt;span class="c"&gt;# -v: Volume mount (old syntax)&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; /host/path:/container/path nginx
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; myvolume:/app/data nginx
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;:/app node:18       &lt;span class="c"&gt;# Mount current directory&lt;/span&gt;

&lt;span class="c"&gt;# --mount: Volume mount (new, preferred syntax)&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--mount&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;bind&lt;/span&gt;,source&lt;span class="o"&gt;=&lt;/span&gt;/host/path,target&lt;span class="o"&gt;=&lt;/span&gt;/container/path nginx
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--mount&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;volume,source&lt;span class="o"&gt;=&lt;/span&gt;myvolume,target&lt;span class="o"&gt;=&lt;/span&gt;/data nginx
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--mount&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;tmpfs,target&lt;span class="o"&gt;=&lt;/span&gt;/tmp nginx

&lt;span class="c"&gt;# -e: Environment variables&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;NODE_ENV&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;production node:18
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;DB_HOST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;localhost &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;DB_PORT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5432 myapp
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--env-file&lt;/span&gt; .env myapp        &lt;span class="c"&gt;# Load from .env file&lt;/span&gt;

&lt;span class="c"&gt;# --name: Give the container a name&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; my-api myapp:v1.0

&lt;span class="c"&gt;# --rm: Automatically remove when it exits&lt;/span&gt;
docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; ubuntu &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"hello"&lt;/span&gt;        &lt;span class="c"&gt;# Perfect for one-off commands&lt;/span&gt;
docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;:/app node:18 npm &lt;span class="nb"&gt;test&lt;/span&gt;

&lt;span class="c"&gt;# --network: Connect to a network&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--network&lt;/span&gt; mynetwork nginx
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--network&lt;/span&gt; host nginx         &lt;span class="c"&gt;# Use host machine's network&lt;/span&gt;

&lt;span class="c"&gt;# --restart: Restart policy&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--restart&lt;/span&gt; no nginx              &lt;span class="c"&gt;# Never restart (default)&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--restart&lt;/span&gt; always nginx          &lt;span class="c"&gt;# Always restart&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--restart&lt;/span&gt; on-failure nginx      &lt;span class="c"&gt;# Restart only on failure&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--restart&lt;/span&gt; on-failure:3 nginx    &lt;span class="c"&gt;# Max 3 retries&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--restart&lt;/span&gt; unless-stopped nginx  &lt;span class="c"&gt;# Restart unless manually stopped&lt;/span&gt;

&lt;span class="c"&gt;# -it: Interactive Terminal (enter the container)&lt;/span&gt;
docker run &lt;span class="nt"&gt;-it&lt;/span&gt; ubuntu bash               &lt;span class="c"&gt;# Start bash session&lt;/span&gt;
docker run &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;--rm&lt;/span&gt; ubuntu bash          &lt;span class="c"&gt;# Session ends → container deleted&lt;/span&gt;
docker run &lt;span class="nt"&gt;-it&lt;/span&gt; node:18 node              &lt;span class="c"&gt;# Node.js REPL&lt;/span&gt;

&lt;span class="c"&gt;# Resource Limits (CRITICAL IN PRODUCTION!)&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--memory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;512m nginx              &lt;span class="c"&gt;# Max 512MB RAM&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--memory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;512m &lt;span class="nt"&gt;--memory-swap&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1g nginx  &lt;span class="c"&gt;# RAM + swap limit&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--cpus&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0.5 nginx                 &lt;span class="c"&gt;# 50% of one CPU&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--cpus&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2 nginx                   &lt;span class="c"&gt;# 2 full CPUs&lt;/span&gt;

&lt;span class="c"&gt;# --user: Run as a specific user (security)&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--user&lt;/span&gt; 1000:1000 nginx
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--user&lt;/span&gt; nobody nginx

&lt;span class="c"&gt;# Real-world Production Example — all together&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; my-react-app &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-p&lt;/span&gt; 3000:3000 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;NODE_ENV&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;production &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;API_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://api.myapp.com &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; /var/log/app:/app/logs &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--memory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;256m &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--cpus&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0.5 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--restart&lt;/span&gt; unless-stopped &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--network&lt;/span&gt; app-network &lt;span class="se"&gt;\&lt;/span&gt;
  myreactapp:v1.2.3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note (Production):&lt;/strong&gt; Without resource limits, a single buggy container can consume all server memory. A memory limit means the OOM killer will kill the container — not the entire server. CPU limits ensure fair sharing between containers.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  docker ps — List Containers
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Running containers only&lt;/span&gt;
docker ps

&lt;span class="c"&gt;# ALL containers (including stopped)&lt;/span&gt;
docker ps &lt;span class="nt"&gt;-a&lt;/span&gt;

&lt;span class="c"&gt;# IDs only (useful in scripts)&lt;/span&gt;
docker ps &lt;span class="nt"&gt;-q&lt;/span&gt;

&lt;span class="c"&gt;# Last 5 created containers&lt;/span&gt;
docker ps &lt;span class="nt"&gt;-n&lt;/span&gt; 5

&lt;span class="c"&gt;# Custom format&lt;/span&gt;
docker ps &lt;span class="nt"&gt;--format&lt;/span&gt; &lt;span class="s2"&gt;"table {{.ID}}&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s2"&gt;{{.Names}}&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s2"&gt;{{.Status}}&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s2"&gt;{{.Ports}}"&lt;/span&gt;
docker ps &lt;span class="nt"&gt;--format&lt;/span&gt; &lt;span class="s2"&gt;"{{.Names}}: {{.Status}}"&lt;/span&gt;

&lt;span class="c"&gt;# Filter by status&lt;/span&gt;
docker ps &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="nv"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;running
docker ps &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="nv"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;exited
docker ps &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;myapp
docker ps &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="nv"&gt;ancestor&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;nginx       &lt;span class="c"&gt;# Containers using nginx image&lt;/span&gt;

&lt;span class="c"&gt;# Show container sizes&lt;/span&gt;
docker ps &lt;span class="nt"&gt;-s&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  docker stop / start / restart
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Graceful stop — sends SIGTERM, waits 10 sec, then SIGKILL&lt;/span&gt;
docker stop myapp
docker stop &lt;span class="nt"&gt;--time&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;30 myapp       &lt;span class="c"&gt;# Wait 30 sec before SIGKILL&lt;/span&gt;

&lt;span class="c"&gt;# Stop multiple containers at once&lt;/span&gt;
docker stop container1 container2 container3

&lt;span class="c"&gt;# Stop all running containers&lt;/span&gt;
docker stop &lt;span class="si"&gt;$(&lt;/span&gt;docker ps &lt;span class="nt"&gt;-q&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Start a stopped container&lt;/span&gt;
docker start myapp
docker start &lt;span class="nt"&gt;-a&lt;/span&gt; myapp             &lt;span class="c"&gt;# Start + attach to output&lt;/span&gt;

&lt;span class="c"&gt;# Restart = stop + start&lt;/span&gt;
docker restart myapp
docker restart &lt;span class="nt"&gt;--time&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5 myapp     &lt;span class="c"&gt;# Wait 5 sec before SIGKILL&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  docker pause / unpause — Freeze a Container
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Freeze container (uses cgroups freezer)&lt;/span&gt;
&lt;span class="c"&gt;# Container state is preserved, no CPU consumed&lt;/span&gt;
docker pause myapp

&lt;span class="c"&gt;# Unfreeze — container continues from where it left off&lt;/span&gt;
docker unpause myapp

&lt;span class="c"&gt;# When to use:&lt;/span&gt;
&lt;span class="c"&gt;# - Load testing: pause one container, test another&lt;/span&gt;
&lt;span class="c"&gt;# - Debug: capture exact state without losing data&lt;/span&gt;
&lt;span class="c"&gt;# - Resource saving: temporarily free CPU&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;code&gt;docker pause&lt;/code&gt; = process freeze (still in memory, state preserved, no CPU). &lt;code&gt;docker stop&lt;/code&gt; = process terminate (SIGTERM → SIGKILL). Unpause is very fast because the process doesn't restart — it just unfreezes.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  docker kill — Immediate Termination
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Immediate kill (SIGKILL — no grace period)&lt;/span&gt;
docker &lt;span class="nb"&gt;kill &lt;/span&gt;myapp

&lt;span class="c"&gt;# Send a specific signal&lt;/span&gt;
docker &lt;span class="nb"&gt;kill&lt;/span&gt; &lt;span class="nt"&gt;--signal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;SIGTERM myapp    &lt;span class="c"&gt;# Graceful shutdown request&lt;/span&gt;
docker &lt;span class="nb"&gt;kill&lt;/span&gt; &lt;span class="nt"&gt;--signal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;SIGHUP myapp     &lt;span class="c"&gt;# Nginx config reload (without restart!)&lt;/span&gt;
docker &lt;span class="nb"&gt;kill&lt;/span&gt; &lt;span class="nt"&gt;--signal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;9 myapp          &lt;span class="c"&gt;# SIGKILL (same as default kill)&lt;/span&gt;
docker &lt;span class="nb"&gt;kill&lt;/span&gt; &lt;span class="nt"&gt;--signal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;SIGUSR1 myapp    &lt;span class="c"&gt;# App-specific custom signal&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  docker rm — Remove Containers
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Remove a stopped container&lt;/span&gt;
docker &lt;span class="nb"&gt;rm &lt;/span&gt;myapp

&lt;span class="c"&gt;# Force remove a running container (think before you do this!)&lt;/span&gt;
docker &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; myapp

&lt;span class="c"&gt;# Remove container and its anonymous volume&lt;/span&gt;
docker &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; myapp

&lt;span class="c"&gt;# Remove all stopped containers at once (recommended!)&lt;/span&gt;
docker container prune
docker &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;docker ps &lt;span class="nt"&gt;-aq&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="nv"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;exited&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Remove ALL containers (running + stopped) — nuclear option!&lt;/span&gt;
docker &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;docker ps &lt;span class="nt"&gt;-aq&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  docker logs — View Logs (First Debug Tool)
&lt;/h3&gt;

&lt;p&gt;When something is not working, look at logs first!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Basic logs&lt;/span&gt;
docker logs myapp

&lt;span class="c"&gt;# Follow (like tail -f, real-time updates)&lt;/span&gt;
docker logs &lt;span class="nt"&gt;-f&lt;/span&gt; myapp

&lt;span class="c"&gt;# Last 100 lines only&lt;/span&gt;
docker logs &lt;span class="nt"&gt;--tail&lt;/span&gt; 100 myapp

&lt;span class="c"&gt;# Show with timestamps&lt;/span&gt;
docker logs &lt;span class="nt"&gt;-t&lt;/span&gt; myapp

&lt;span class="c"&gt;# Logs since a specific time&lt;/span&gt;
docker logs &lt;span class="nt"&gt;--since&lt;/span&gt; 2024-01-01 myapp
docker logs &lt;span class="nt"&gt;--since&lt;/span&gt; 1h myapp              &lt;span class="c"&gt;# Last 1 hour of logs&lt;/span&gt;

&lt;span class="c"&gt;# Most useful combination — last 50 lines follow with timestamps&lt;/span&gt;
docker logs &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; &lt;span class="nt"&gt;--tail&lt;/span&gt; 50 myapp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; &lt;code&gt;docker logs&lt;/code&gt; works when the application writes to stdout/stderr. If the app writes to a file (e.g., &lt;code&gt;/var/log/app.log&lt;/code&gt;), use &lt;code&gt;docker exec -it myapp tail -f /var/log/app.log&lt;/code&gt;, or mount a volume and read from the host.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  docker exec — Enter a Running Container
&lt;/h3&gt;

&lt;p&gt;This command is used daily for debugging. Enter a running container and investigate.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Run a command inside the container&lt;/span&gt;
docker &lt;span class="nb"&gt;exec &lt;/span&gt;myapp &lt;span class="nb"&gt;ls&lt;/span&gt; /app
docker &lt;span class="nb"&gt;exec &lt;/span&gt;myapp &lt;span class="nb"&gt;cat&lt;/span&gt; /etc/nginx/nginx.conf

&lt;span class="c"&gt;# Interactive bash session (most common use case)&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; myapp bash

&lt;span class="c"&gt;# Alpine-based containers: use sh (bash not available)&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; myapp sh

&lt;span class="c"&gt;# Login as root (when full access is needed)&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;--user&lt;/span&gt; root myapp bash

&lt;span class="c"&gt;# With environment variable&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;DEBUG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true &lt;/span&gt;myapp node debug.js

&lt;span class="c"&gt;# Real-world examples&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; postgres psql &lt;span class="nt"&gt;-U&lt;/span&gt; admin &lt;span class="nt"&gt;-d&lt;/span&gt; mydb    &lt;span class="c"&gt;# Enter PostgreSQL&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; redis redis-cli                   &lt;span class="c"&gt;# Open Redis CLI&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; nginx nginx &lt;span class="nt"&gt;-t&lt;/span&gt;                    &lt;span class="c"&gt;# Test nginx config&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; nginx nginx &lt;span class="nt"&gt;-s&lt;/span&gt; reload             &lt;span class="c"&gt;# Gracefully reload nginx&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; myapp &lt;span class="nb"&gt;cat&lt;/span&gt; /app/config.json        &lt;span class="c"&gt;# Read config file&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; myapp &lt;span class="nb"&gt;env&lt;/span&gt;                         &lt;span class="c"&gt;# View all env variables&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; myapp ps aux                      &lt;span class="c"&gt;# View running processes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; &lt;code&gt;docker exec&lt;/code&gt; starts a new process inside the container. &lt;code&gt;docker attach&lt;/code&gt; connects to the container's main process — pressing Ctrl+C will STOP the container! For debugging, always use &lt;code&gt;exec&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  docker inspect — Extract Full Details
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Full JSON info&lt;/span&gt;
docker inspect myapp

&lt;span class="c"&gt;# Extract specific info with Go templates&lt;/span&gt;
docker inspect &lt;span class="nt"&gt;--format&lt;/span&gt; &lt;span class="s1"&gt;'{{.NetworkSettings.IPAddress}}'&lt;/span&gt; myapp
docker inspect &lt;span class="nt"&gt;--format&lt;/span&gt; &lt;span class="s1"&gt;'{{.Config.Env}}'&lt;/span&gt; myapp
docker inspect &lt;span class="nt"&gt;--format&lt;/span&gt; &lt;span class="s1"&gt;'{{.Config.Image}}'&lt;/span&gt; myapp
docker inspect &lt;span class="nt"&gt;--format&lt;/span&gt; &lt;span class="s1"&gt;'{{.State.StartedAt}}'&lt;/span&gt; myapp

&lt;span class="c"&gt;# Port mappings&lt;/span&gt;
docker inspect &lt;span class="nt"&gt;--format&lt;/span&gt; &lt;span class="s1"&gt;'{{json .NetworkSettings.Ports}}'&lt;/span&gt; myapp | jq

&lt;span class="c"&gt;# Volume/mount info&lt;/span&gt;
docker inspect &lt;span class="nt"&gt;--format&lt;/span&gt; &lt;span class="s1"&gt;'{{json .Mounts}}'&lt;/span&gt; myapp | jq

&lt;span class="c"&gt;# Container exit code (for debugging)&lt;/span&gt;
docker inspect &lt;span class="nt"&gt;--format&lt;/span&gt; &lt;span class="s1"&gt;'{{.State.ExitCode}}'&lt;/span&gt; myapp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  docker top, docker stats, docker cp, docker commit
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# View processes inside container (from host, without entering container)&lt;/span&gt;
docker top myapp
docker top myapp aux
docker top myapp &lt;span class="nt"&gt;-ef&lt;/span&gt;

&lt;span class="c"&gt;# Real-time resource usage (live dashboard)&lt;/span&gt;
docker stats
docker stats myapp
docker stats &lt;span class="nt"&gt;--no-stream&lt;/span&gt;          &lt;span class="c"&gt;# One snapshot&lt;/span&gt;
docker stats &lt;span class="nt"&gt;--format&lt;/span&gt; &lt;span class="s2"&gt;"table {{.Name}}&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s2"&gt;{{.CPUPerc}}&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s2"&gt;{{.MemUsage}}&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s2"&gt;{{.NetIO}}"&lt;/span&gt;

&lt;span class="c"&gt;# Copy files between container and host&lt;/span&gt;
docker &lt;span class="nb"&gt;cp &lt;/span&gt;myapp:/app/config.json ./config.json     &lt;span class="c"&gt;# Container → host&lt;/span&gt;
docker &lt;span class="nb"&gt;cp&lt;/span&gt; ./config.json myapp:/app/config.json     &lt;span class="c"&gt;# Host → container&lt;/span&gt;
docker &lt;span class="nb"&gt;cp &lt;/span&gt;myapp:/var/log/app.log ./app.log
&lt;span class="c"&gt;# Works with stopped containers too!&lt;/span&gt;

&lt;span class="c"&gt;# Create a new image from a modified container&lt;/span&gt;
docker commit myapp myapp-modified:v2
docker commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Added custom nginx config"&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="s2"&gt;"Author"&lt;/span&gt; myapp myapp:custom
&lt;span class="c"&gt;# NOTE: Use Dockerfiles in production — docker commit is not reproducible!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Container Operations — Advanced Concepts
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Foreground vs Background — Attached vs Detached Mode
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Foreground (attached) — output visible, Ctrl+C stops the container&lt;/span&gt;
docker run nginx
&lt;span class="c"&gt;# Terminal is blocked, nginx logs appear here&lt;/span&gt;

&lt;span class="c"&gt;# Background (detached) — runs in background, terminal is free&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; nginx
&lt;span class="c"&gt;# Returns container ID, you can do other work&lt;/span&gt;

&lt;span class="c"&gt;# Attach to a running container's output&lt;/span&gt;
docker attach myapp
&lt;span class="c"&gt;# WARNING: Ctrl+C stops the container!&lt;/span&gt;
&lt;span class="c"&gt;# To detach without stopping: Ctrl+P then Ctrl+Q&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Detaching Without Stopping — Critical Shortcut
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Start an interactive session&lt;/span&gt;
docker run &lt;span class="nt"&gt;-it&lt;/span&gt; ubuntu bash

&lt;span class="c"&gt;# To leave WITHOUT stopping the container:&lt;/span&gt;
&lt;span class="c"&gt;# Press Ctrl+P then Ctrl+Q (both quickly)&lt;/span&gt;
&lt;span class="c"&gt;# Container keeps running, you get your terminal back&lt;/span&gt;

&lt;span class="c"&gt;# Return later&lt;/span&gt;
docker attach ubuntu_container_name

&lt;span class="c"&gt;# exec vs attach — which to use?&lt;/span&gt;
&lt;span class="c"&gt;# exec — starts a new process (SAFE, always use this)&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; myapp bash

&lt;span class="c"&gt;# attach — connects to main process (RISKY for debugging)&lt;/span&gt;
docker attach myapp   &lt;span class="c"&gt;# Ctrl+C = container STOP!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; New users often use &lt;code&gt;docker attach&lt;/code&gt; and then press Ctrl+C thinking they are only detaching from the terminal — but the container STOPS! In production, always use &lt;code&gt;docker exec -it myapp bash&lt;/code&gt;. If you must use attach, detach with &lt;code&gt;Ctrl+P Ctrl+Q&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Container Exit Codes
&lt;/h3&gt;

&lt;p&gt;Exit codes tell you why a container stopped. Visible in &lt;code&gt;docker ps -a&lt;/code&gt; in the STATUS column as "Exited (137)".&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Exit Code&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;th&gt;Common Cause&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;0&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Success&lt;/td&gt;
&lt;td&gt;Container completed its task successfully&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;1&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;General Error&lt;/td&gt;
&lt;td&gt;Application crash, unhandled exception, startup failure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;125&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Docker daemon error&lt;/td&gt;
&lt;td&gt;Invalid &lt;code&gt;docker run&lt;/code&gt; options, Docker internal error&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;126&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Command can't execute&lt;/td&gt;
&lt;td&gt;Permission error — command found but not executable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;127&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Command not found&lt;/td&gt;
&lt;td&gt;Wrong command or PATH issue, typo in CMD/ENTRYPOINT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;130&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Ctrl+C (SIGINT)&lt;/td&gt;
&lt;td&gt;Manual interruption&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;137&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;SIGKILL (128+9)&lt;/td&gt;
&lt;td&gt;OOM killer or docker kill / docker stop timeout&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;139&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Segmentation fault&lt;/td&gt;
&lt;td&gt;Application memory error&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;143&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;SIGTERM (128+15)&lt;/td&gt;
&lt;td&gt;docker stop — graceful shutdown signal&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Signal-based exit codes = 128 + signal_number. SIGKILL = signal 9, exit code = 137. SIGTERM = signal 15, exit code = 143. Exit 137 almost always means OOM or force kill.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Restart Policies — Detailed
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# no (default) — Never restart&lt;/span&gt;
docker run &lt;span class="nt"&gt;--restart&lt;/span&gt; no nginx
&lt;span class="c"&gt;# Use case: one-off scripts, batch jobs, dev environment&lt;/span&gt;

&lt;span class="c"&gt;# always — Always restart (ignores exit code)&lt;/span&gt;
docker run &lt;span class="nt"&gt;--restart&lt;/span&gt; always nginx
&lt;span class="c"&gt;# Note: Also auto-starts on Docker daemon restart!&lt;/span&gt;
&lt;span class="c"&gt;# Use case: critical services that must always be running&lt;/span&gt;

&lt;span class="c"&gt;# on-failure — Restart only on failure (non-zero exit)&lt;/span&gt;
docker run &lt;span class="nt"&gt;--restart&lt;/span&gt; on-failure nginx         &lt;span class="c"&gt;# Unlimited retries&lt;/span&gt;
docker run &lt;span class="nt"&gt;--restart&lt;/span&gt; on-failure:5 nginx       &lt;span class="c"&gt;# Max 5 retries, then DEAD&lt;/span&gt;
&lt;span class="c"&gt;# Use case: services that may occasionally crash&lt;/span&gt;

&lt;span class="c"&gt;# unless-stopped — Like always, but respects manual stop&lt;/span&gt;
docker run &lt;span class="nt"&gt;--restart&lt;/span&gt; unless-stopped nginx
&lt;span class="c"&gt;# docker stop → will NOT restart on daemon restart&lt;/span&gt;
&lt;span class="c"&gt;# Use case: Production services (RECOMMENDED for most cases)&lt;/span&gt;

&lt;span class="c"&gt;# Change policy on a running container (no restart required)&lt;/span&gt;
docker update &lt;span class="nt"&gt;--restart&lt;/span&gt; unless-stopped myapp
docker update &lt;span class="nt"&gt;--restart&lt;/span&gt; no myapp              &lt;span class="c"&gt;# Disable restart&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Policy&lt;/th&gt;
&lt;th&gt;Restart on crash?&lt;/th&gt;
&lt;th&gt;Restart on daemon restart?&lt;/th&gt;
&lt;th&gt;Restart after docker stop?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;no&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Never&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;always&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes (even after docker stop!)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;on-failure&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Yes (non-zero exit only)&lt;/td&gt;
&lt;td&gt;Yes (if was running)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;unless-stopped&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No (respects manual stop)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Resource Monitoring
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Real-time stats dashboard (updates every 1 second)&lt;/span&gt;
watch &lt;span class="nt"&gt;-n&lt;/span&gt; 1 docker stats &lt;span class="nt"&gt;--no-stream&lt;/span&gt;

&lt;span class="c"&gt;# Docker system overall usage&lt;/span&gt;
docker system &lt;span class="nb"&gt;df&lt;/span&gt;              &lt;span class="c"&gt;# Disk usage — images, containers, volumes, build cache&lt;/span&gt;
docker system events          &lt;span class="c"&gt;# Live event stream&lt;/span&gt;
docker system info            &lt;span class="c"&gt;# Complete Docker system info&lt;/span&gt;

&lt;span class="c"&gt;# Monitor events (powerful debugging tool)&lt;/span&gt;
docker events                              &lt;span class="c"&gt;# All events real-time&lt;/span&gt;
docker events &lt;span class="nt"&gt;--filter&lt;/span&gt; &lt;span class="s1"&gt;'type=container'&lt;/span&gt;   &lt;span class="c"&gt;# Container events only&lt;/span&gt;
docker events &lt;span class="nt"&gt;--filter&lt;/span&gt; &lt;span class="s1"&gt;'event=die'&lt;/span&gt;        &lt;span class="c"&gt;# Container deaths&lt;/span&gt;
docker events &lt;span class="nt"&gt;--filter&lt;/span&gt; &lt;span class="s1"&gt;'event=start'&lt;/span&gt;      &lt;span class="c"&gt;# Container starts&lt;/span&gt;
docker events &lt;span class="nt"&gt;--since&lt;/span&gt; 1h                  &lt;span class="c"&gt;# Events from last 1 hour&lt;/span&gt;

&lt;span class="c"&gt;# Container health check status&lt;/span&gt;
docker inspect &lt;span class="nt"&gt;--format&lt;/span&gt; &lt;span class="s1"&gt;'{{.State.Health.Status}}'&lt;/span&gt; myapp
docker inspect &lt;span class="nt"&gt;--format&lt;/span&gt; &lt;span class="s1"&gt;'{{json .State.Health}}'&lt;/span&gt; myapp | jq
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Cleanup Commands
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check disk usage first&lt;/span&gt;
docker system &lt;span class="nb"&gt;df&lt;/span&gt;
&lt;span class="c"&gt;# TYPE            TOTAL     ACTIVE    SIZE      RECLAIMABLE&lt;/span&gt;
&lt;span class="c"&gt;# Images          25        3         12.5GB    10.2GB (82%)&lt;/span&gt;
&lt;span class="c"&gt;# Containers      8         2         150MB     130MB (86%)&lt;/span&gt;
&lt;span class="c"&gt;# Local Volumes   15        3         4.2GB     3.8GB (90%)&lt;/span&gt;
&lt;span class="c"&gt;# Build Cache     -         -         2.1GB     2.1GB&lt;/span&gt;

&lt;span class="c"&gt;# Individual cleanup (specific resources)&lt;/span&gt;
docker container prune                      &lt;span class="c"&gt;# Remove stopped containers&lt;/span&gt;
docker image prune                          &lt;span class="c"&gt;# Remove dangling images&lt;/span&gt;
docker image prune &lt;span class="nt"&gt;-a&lt;/span&gt;                       &lt;span class="c"&gt;# Remove all unused images&lt;/span&gt;
docker volume prune                         &lt;span class="c"&gt;# Remove unused volumes&lt;/span&gt;
docker network prune                        &lt;span class="c"&gt;# Remove unused networks&lt;/span&gt;

&lt;span class="c"&gt;# Nuclear options — use carefully!&lt;/span&gt;
docker system prune                         &lt;span class="c"&gt;# Remove everything unused (no volumes)&lt;/span&gt;
docker system prune &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nt"&gt;--volumes&lt;/span&gt;            &lt;span class="c"&gt;# Remove everything including volumes&lt;/span&gt;

&lt;span class="c"&gt;# Non-interactive (for CI/CD scripts)&lt;/span&gt;
docker system prune &lt;span class="nt"&gt;-f&lt;/span&gt;
docker system prune &lt;span class="nt"&gt;-af&lt;/span&gt; &lt;span class="nt"&gt;--volumes&lt;/span&gt;           &lt;span class="c"&gt;# Full nuclear, no confirmation&lt;/span&gt;

&lt;span class="c"&gt;# Smart cleanup — only remove old items&lt;/span&gt;
docker container prune &lt;span class="nt"&gt;--filter&lt;/span&gt; &lt;span class="s2"&gt;"until=24h"&lt;/span&gt;    &lt;span class="c"&gt;# Containers older than 24 hours&lt;/span&gt;
docker image prune &lt;span class="nt"&gt;--filter&lt;/span&gt; &lt;span class="s2"&gt;"until=168h"&lt;/span&gt;       &lt;span class="c"&gt;# Dangling images older than 1 week&lt;/span&gt;
docker system prune &lt;span class="nt"&gt;--filter&lt;/span&gt; &lt;span class="s2"&gt;"until=72h"&lt;/span&gt;       &lt;span class="c"&gt;# Everything older than 3 days&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution (Production Cleanup Strategy):&lt;/strong&gt; On CI/CD servers, set up a daily cron job: &lt;code&gt;0 2 * * * docker system prune -f&lt;/code&gt;. On production servers, only &lt;code&gt;docker container prune&lt;/code&gt; and &lt;code&gt;docker image prune&lt;/code&gt; are safe — &lt;strong&gt;never run &lt;code&gt;docker volume prune&lt;/code&gt; on production!&lt;/strong&gt; An accidental &lt;code&gt;docker volume prune&lt;/code&gt; has permanently deleted production data for multiple teams.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tip (Interview — SIGTERM vs SIGKILL):&lt;/strong&gt; &lt;code&gt;docker stop&lt;/code&gt; first sends &lt;strong&gt;SIGTERM&lt;/strong&gt; — a graceful shutdown request. The application has 10 seconds to clean up (close connections, complete in-flight requests, flush data). If it doesn't exit within 10 seconds, &lt;strong&gt;SIGKILL&lt;/strong&gt; is sent — forced termination, no cleanup. &lt;code&gt;docker kill&lt;/code&gt; sends &lt;strong&gt;SIGKILL&lt;/strong&gt; directly — no grace period. Use &lt;code&gt;docker stop&lt;/code&gt; in production so applications can shut down properly. In Node.js: &lt;code&gt;process.on('SIGTERM', gracefulShutdown)&lt;/code&gt;. Exit codes: SIGTERM = 143, SIGKILL = 137.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Dockerfile Basics
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What is a Dockerfile?
&lt;/h3&gt;

&lt;p&gt;A Dockerfile is a text file containing instructions for how to build an image. Think of it as a recipe card — ingredients and steps. Each instruction creates a layer, and layers together form the final Docker image.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why build a custom image?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your app requires a specific Node/Python version&lt;/li&gt;
&lt;li&gt;Custom environment variables or config are needed&lt;/li&gt;
&lt;li&gt;Pre-installed tools (curl, git, etc.) are required&lt;/li&gt;
&lt;li&gt;You need a production-ready, minimal image&lt;/li&gt;
&lt;li&gt;Security hardening — non-root user, read-only filesystem&lt;/li&gt;
&lt;li&gt;Reproducible builds — every developer gets the same environment&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Where to put the Dockerfile
&lt;/h3&gt;

&lt;p&gt;Usually in the project root — the same directory as &lt;code&gt;package.json&lt;/code&gt; or &lt;code&gt;requirements.txt&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my-project/
├── Dockerfile          ← here
├── Dockerfile.dev      ← for dev environment
├── Dockerfile.prod     ← for prod environment
├── .dockerignore       ← CRITICAL! Never forget this
├── package.json
├── src/
└── dist/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  docker build — Building an Image
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Build with current dir as context&lt;/span&gt;
docker build &lt;span class="nt"&gt;-t&lt;/span&gt; myapp:latest &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="c"&gt;# Build with specific Dockerfile&lt;/span&gt;
docker build &lt;span class="nt"&gt;-f&lt;/span&gt; Dockerfile.prod &lt;span class="nt"&gt;-t&lt;/span&gt; myapp:prod &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="c"&gt;# Build from a remote Git repo&lt;/span&gt;
docker build &lt;span class="nt"&gt;-t&lt;/span&gt; myapp https://github.com/user/repo.git

&lt;span class="c"&gt;# Build with no cache (fresh build)&lt;/span&gt;
docker build &lt;span class="nt"&gt;--no-cache&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; myapp &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="c"&gt;# Build with build args&lt;/span&gt;
docker build &lt;span class="nt"&gt;--build-arg&lt;/span&gt; &lt;span class="nv"&gt;NODE_ENV&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;production &lt;span class="nt"&gt;-t&lt;/span&gt; myapp &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="c"&gt;# Build with multiple tags&lt;/span&gt;
docker build &lt;span class="nt"&gt;-t&lt;/span&gt; myapp:latest &lt;span class="nt"&gt;-t&lt;/span&gt; myapp:v1.0.0 &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="c"&gt;# Build with verbose output (useful for CI)&lt;/span&gt;
docker build &lt;span class="nt"&gt;--progress&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;plain &lt;span class="nt"&gt;-t&lt;/span&gt; myapp &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; When you write &lt;code&gt;docker build -t myapp .&lt;/code&gt;, the &lt;code&gt;.&lt;/code&gt; (dot) is the build context — all files in this directory are sent to the Docker daemon. If your &lt;code&gt;node_modules&lt;/code&gt; is 500MB and you have no &lt;code&gt;.dockerignore&lt;/code&gt;, that entire directory gets sent. This is why &lt;code&gt;.dockerignore&lt;/code&gt; is critical.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  .dockerignore — Never Skip This
&lt;/h3&gt;

&lt;p&gt;This file works exactly like &lt;code&gt;.gitignore&lt;/code&gt; — it tells the Docker daemon which files to exclude from the build context.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; Never let &lt;code&gt;.env&lt;/code&gt; files into a Docker image. If &lt;code&gt;AWS_SECRET_KEY&lt;/code&gt; or a database password ends up in an image, anyone can run &lt;code&gt;docker history myapp&lt;/code&gt; and see it — even if you deleted it in a later layer!&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# .dockerignore - put this in EVERY project!&lt;/span&gt;

&lt;span class="c"&gt;# Dependencies (will be installed inside container)&lt;/span&gt;
node_modules/
vendor/
__pycache__/
*.pyc
.pytest_cache/
venv/
.venv/

# Build outputs
dist/
build/
.next/
out/
target/

# Environment files (SECURITY RISK!)
.env
.env.local
.env.*.local
.env.development
.env.production
*.pem
*.key
secrets/

# Version control
.git/
.gitignore

&lt;span class="c"&gt;# Logs&lt;/span&gt;
*.log
logs/

# OS files
.DS_Store
Thumbs.db

&lt;span class="c"&gt;# IDE/Editor files&lt;/span&gt;
.idea/
.vscode/

# CI/CD &amp;amp; Docker meta
.github/
.dockerignore
Dockerfile*

# Documentation
README.md
*.md
docs/

# Test &amp;amp; Coverage
coverage/
.nyc_output
__tests__/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Build Cache
&lt;/h3&gt;

&lt;p&gt;Docker caches every layer. If a layer has not changed, Docker uses the cache — builds become very fast.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;th&gt;Speed&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Layer unchanged&lt;/td&gt;
&lt;td&gt;Cache HIT&lt;/td&gt;
&lt;td&gt;Instant&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Layer changed&lt;/td&gt;
&lt;td&gt;Cache MISS&lt;/td&gt;
&lt;td&gt;Slow (rebuild)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;After a cache miss, all subsequent layers&lt;/td&gt;
&lt;td&gt;Force rebuild&lt;/td&gt;
&lt;td&gt;Slow&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;--no-cache&lt;/code&gt; flag&lt;/td&gt;
&lt;td&gt;Skip all cache&lt;/td&gt;
&lt;td&gt;Slowest&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; What changes least goes on top. What changes most (source code) goes at the bottom. If &lt;code&gt;package.json&lt;/code&gt; hasn't changed, npm install won't re-run — only source code copy will.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Image Naming Conventions
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Tag formats&lt;/span&gt;
myapp:latest           &lt;span class="c"&gt;# ❌ Avoid in production!&lt;/span&gt;
myapp:v1.0.0           &lt;span class="c"&gt;# ✅ Semantic version (recommended)&lt;/span&gt;
myapp:1.0.0-alpine     &lt;span class="c"&gt;# ✅ Version + variant&lt;/span&gt;
myapp:2024-01-15       &lt;span class="c"&gt;# ✅ Date-based&lt;/span&gt;
myapp:abc1234          &lt;span class="c"&gt;# ✅ Git commit hash (traceable)&lt;/span&gt;
myapp:prod-v1.2.3      &lt;span class="c"&gt;# ✅ Environment-specific&lt;/span&gt;

&lt;span class="c"&gt;# With registry&lt;/span&gt;
docker.io/myuser/myapp:v1.0.0          &lt;span class="c"&gt;# Docker Hub&lt;/span&gt;
ghcr.io/myorg/myapp:v1.0.0            &lt;span class="c"&gt;# GitHub Container Registry&lt;/span&gt;
123456.dkr.ecr.us-east-1.amazonaws.com/myapp:v1.0.0  &lt;span class="c"&gt;# AWS ECR&lt;/span&gt;

&lt;span class="c"&gt;# Build with git commit tag (common in CI/CD)&lt;/span&gt;
docker build &lt;span class="nt"&gt;-t&lt;/span&gt; myapp:&lt;span class="si"&gt;$(&lt;/span&gt;git rev-parse &lt;span class="nt"&gt;--short&lt;/span&gt; HEAD&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; Using &lt;code&gt;:latest&lt;/code&gt; in production means if you push a new image with &lt;code&gt;:latest&lt;/code&gt;, any server that restarts will pull the new image — without any warning! Always use specific version tags for reproducible deployments.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Dockerfile Instructions — Deep Dive
&lt;/h2&gt;

&lt;h3&gt;
  
  
  FROM — Every Dockerfile Starts Here
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Basic — always specify a version&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; ubuntu:22.04&lt;/span&gt;

&lt;span class="c"&gt;# Specific digest (production best practice — immutable!)&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; ubuntu@sha256:abc123def456...&lt;/span&gt;

&lt;span class="c"&gt;# Multi-stage naming (used later with COPY --from)&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;node:18-alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;builder&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;nginx:alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;production&lt;/span&gt;

&lt;span class="c"&gt;# ARG before FROM (dynamic base image version)&lt;/span&gt;
&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; NODE_VERSION=18&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:${NODE_VERSION}-alpine&lt;/span&gt;

&lt;span class="c"&gt;# ✅ BEST: Pinned version + variant&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:18.19.0-alpine3.19&lt;/span&gt;

&lt;span class="c"&gt;# ❌ BAD: Unpredictable, breaks builds&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:latest&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip (Alpine vs Slim vs Full):&lt;/strong&gt; &lt;strong&gt;alpine&lt;/strong&gt;: uses musl libc, 5MB, fastest — but some native modules may not compile. &lt;strong&gt;slim&lt;/strong&gt;: Debian-based, ~75MB, more compatible. &lt;strong&gt;full&lt;/strong&gt;: Most compatible, but large image. For most Node.js apps: start with &lt;code&gt;node:18-alpine&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  LABEL — Image Metadata
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# ✅ BEST: All together in one layer&lt;/span&gt;
&lt;span class="k"&gt;LABEL&lt;/span&gt;&lt;span class="s"&gt; \&lt;/span&gt;
  maintainer="you@company.com" \
  version="1.0.0" \
  description="React DevOps Guide App" \
  org.opencontainers.image.source="https://github.com/user/repo" \
  org.opencontainers.image.created="2024-01-15" \
  org.opencontainers.image.revision="abc1234"

# Inspect labels
docker inspect myapp --format='{{json .Config.Labels}}'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  RUN — Execute Commands
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Shell form (runs in /bin/sh -c)&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update

&lt;span class="c"&gt;# Exec form (no shell — preferred for clarity)&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"apt-get"&lt;/span&gt;, &lt;span class="s2"&gt;"update"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;

&lt;span class="c"&gt;# ❌ BAD: Multiple RUN = multiple layers = big image&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; curl
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; git

&lt;span class="c"&gt;# ✅ BEST: All in one RUN + clean cache in SAME layer&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="nt"&gt;--no-install-recommends&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;      curl &lt;span class="se"&gt;\
&lt;/span&gt;      git &lt;span class="se"&gt;\
&lt;/span&gt;      build-essential &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /var/lib/apt/lists/&lt;span class="k"&gt;*&lt;/span&gt;   &lt;span class="c"&gt;# CRITICAL: clean apt cache!&lt;/span&gt;

&lt;span class="c"&gt;# Node.js&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm ci &lt;span class="nt"&gt;--only&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;production &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    npm cache clean &lt;span class="nt"&gt;--force&lt;/span&gt;

&lt;span class="c"&gt;# Python&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--no-cache-dir&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt

&lt;span class="c"&gt;# Alpine (apk package manager)&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apk add &lt;span class="nt"&gt;--no-cache&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    curl &lt;span class="se"&gt;\
&lt;/span&gt;    git &lt;span class="se"&gt;\
&lt;/span&gt;    bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; If you don't include &lt;code&gt;rm -rf /var/lib/apt/lists/*&lt;/code&gt;, the apt cache (which can be several MB) stays in the image — size grows unnecessarily. Always clean in the SAME RUN instruction, not in a separate one (cleaning in a separate layer does not reduce the layer size).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  CMD — Default Command
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# ✅ Exec form (PREFERRED — no shell processing, better signal handling)&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["nginx", "-g", "daemon off;"]&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["node", "server.js"]&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["python", "app.py"]&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["uvicorn", "main:app", "--host", "0.0.0.0"]&lt;/span&gt;

&lt;span class="c"&gt;# ❌ Shell form (avoid — shell processing, harder to handle signals)&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; nginx -g "daemon off;"&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; node server.js&lt;/span&gt;

&lt;span class="c"&gt;# ⚠️ Multiple CMD — only the LAST one counts!&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["node", "app.js"]&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["node", "server.js"]   # ← This one runs; the above is ignored&lt;/span&gt;

&lt;span class="c"&gt;# Override at runtime&lt;/span&gt;
docker run myapp node debug.js   &lt;span class="c"&gt;# CMD overridden&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ENTRYPOINT — Fixed Executable
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Exec form (preferred)&lt;/span&gt;
&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["node", "server.js"]&lt;/span&gt;

&lt;span class="c"&gt;# ENTRYPOINT + CMD combination (powerful pattern!)&lt;/span&gt;
&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["node"]&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["server.js"]    # docker run myapp          → node server.js&lt;/span&gt;
                     &lt;span class="c"&gt;# docker run myapp debug.js  → node debug.js (CMD overridden)&lt;/span&gt;
                     &lt;span class="c"&gt;# docker run myapp --version → node --version&lt;/span&gt;

&lt;span class="c"&gt;# Common pattern: entrypoint script for setup logic&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; entrypoint.sh /entrypoint.sh&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x /entrypoint.sh
&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["/entrypoint.sh"]&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["server"]&lt;/span&gt;

&lt;span class="c"&gt;# entrypoint.sh example:&lt;/span&gt;
&lt;span class="c"&gt;#!/bin/sh&lt;/span&gt;
set -e
echo "Starting container..."
# Run migrations, wait for DB, etc.
exec "$@"   # "$@" = pass CMD arguments through
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  CMD vs ENTRYPOINT — Most Important Comparison
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;CMD&lt;/th&gt;
&lt;th&gt;ENTRYPOINT&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Override easily?&lt;/td&gt;
&lt;td&gt;Yes — &lt;code&gt;docker run myapp npm test&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;No — requires &lt;code&gt;--entrypoint&lt;/code&gt; flag&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Purpose&lt;/td&gt;
&lt;td&gt;Provide default arguments&lt;/td&gt;
&lt;td&gt;Set a fixed executable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;When used together&lt;/td&gt;
&lt;td&gt;CMD = default args for ENTRYPOINT&lt;/td&gt;
&lt;td&gt;ENTRYPOINT = command, CMD = args&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Override example&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker run myapp bash&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker run --entrypoint bash myapp&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Use case&lt;/td&gt;
&lt;td&gt;Flexible containers (same image for dev/prod)&lt;/td&gt;
&lt;td&gt;Single-purpose containers&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip (Interview Question):&lt;/strong&gt; "What is the difference between CMD and ENTRYPOINT?" — Short answer: CMD is easily overridden, ENTRYPOINT is not. Use them together — ENTRYPOINT as the executable, CMD as the default arguments.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  COPY and ADD
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# COPY — preferred for most cases&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package.json /app/&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . /app/&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package.json package-lock.json /app/&lt;/span&gt;

&lt;span class="c"&gt;# With ownership (chown)&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --chown=node:node . /app/&lt;/span&gt;

&lt;span class="c"&gt;# From a multi-stage build (POWERFUL!)&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /app/dist /usr/share/nginx/html/&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /app/.next/standalone ./&lt;/span&gt;

&lt;span class="c"&gt;# ADD — supports URLs and auto-extracts tar files&lt;/span&gt;
&lt;span class="k"&gt;ADD&lt;/span&gt;&lt;span class="s"&gt; https://example.com/file.tar.gz /tmp/        # ❌ Prefer curl in RUN&lt;/span&gt;
&lt;span class="k"&gt;ADD&lt;/span&gt;&lt;span class="s"&gt; myarchive.tar.gz /app/                       # ✅ Auto-extracts tar&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;COPY&lt;/th&gt;
&lt;th&gt;ADD&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Local files&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Download from URLs&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Auto-extract tar&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Caching&lt;/td&gt;
&lt;td&gt;Predictable&lt;/td&gt;
&lt;td&gt;Less predictable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Recommended&lt;/td&gt;
&lt;td&gt;For most cases&lt;/td&gt;
&lt;td&gt;Only for tar extraction&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  WORKDIR, ENV, ARG
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# WORKDIR — sets working directory&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /usr/src/app&lt;/span&gt;

&lt;span class="c"&gt;# ❌ BAD: Use cd command&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /app &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm &lt;span class="nb"&gt;install&lt;/span&gt;  &lt;span class="c"&gt;# Only valid for this RUN&lt;/span&gt;

&lt;span class="c"&gt;# ✅ GOOD: Use WORKDIR&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;              &lt;span class="c"&gt;# Runs in /app&lt;/span&gt;

&lt;span class="c"&gt;# ENV — environment variables (build + runtime)&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; NODE_ENV=production \&lt;/span&gt;
    PORT=3000 \
    LOG_LEVEL=info \
    TZ=Asia/Kolkata

&lt;span class="c"&gt;# Override at runtime&lt;/span&gt;
docker run -e NODE_ENV=development myapp
&lt;span class="c"&gt;# ⚠️ Never put secrets in ENV — visible in docker inspect!&lt;/span&gt;

&lt;span class="c"&gt;# ARG — build-time arguments (NOT available at runtime)&lt;/span&gt;
&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; NODE_VERSION=18&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:${NODE_VERSION}-alpine&lt;/span&gt;

&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; BUILD_DATE&lt;/span&gt;
&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; GIT_COMMIT&lt;/span&gt;
&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; APP_VERSION=1.0.0&lt;/span&gt;

&lt;span class="c"&gt;# Pass at build time&lt;/span&gt;
docker build \
  --build-arg NODE_VERSION=20 \
  --build-arg BUILD_DATE=$(date -u +%Y-%m-%dT%H:%M:%SZ) \
  --build-arg GIT_COMMIT=$(git rev-parse --short HEAD) \
  -t myapp .

# Convert ARG to ENV (to make it available at runtime)
&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; REACT_APP_API_URL&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; REACT_APP_API_URL=$REACT_APP_API_URL&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;ARG&lt;/th&gt;
&lt;th&gt;ENV&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Available at&lt;/td&gt;
&lt;td&gt;Build time only&lt;/td&gt;
&lt;td&gt;Build + Runtime&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Persists in container&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Can use before FROM&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Visibility&lt;/td&gt;
&lt;td&gt;In layer history&lt;/td&gt;
&lt;td&gt;In &lt;code&gt;docker inspect&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  EXPOSE, VOLUME, USER
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# EXPOSE — documentation only (does NOT actually open the port!)&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 3000&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 80 443&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 8080/tcp&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 53/udp&lt;/span&gt;

&lt;span class="c"&gt;# Actual port publishing requires -p flag at runtime:&lt;/span&gt;
docker run -p 8080:3000 myapp   &lt;span class="c"&gt;# host:container&lt;/span&gt;

&lt;span class="c"&gt;# VOLUME — declare mount points&lt;/span&gt;
&lt;span class="k"&gt;VOLUME&lt;/span&gt;&lt;span class="s"&gt; /app/data&lt;/span&gt;
&lt;span class="k"&gt;VOLUME&lt;/span&gt;&lt;span class="s"&gt; /var/lib/postgresql/data&lt;/span&gt;
&lt;span class="k"&gt;VOLUME&lt;/span&gt;&lt;span class="s"&gt; ["/app/logs", "/app/uploads"]&lt;/span&gt;

&lt;span class="c"&gt;# In production, use named volumes:&lt;/span&gt;
docker run -v postgres_data:/var/lib/postgresql/data postgres

&lt;span class="c"&gt;# USER — run as non-root (security!)&lt;/span&gt;
&lt;span class="c"&gt;# ❌ BAD: Running as root (default if USER not specified)&lt;/span&gt;

&lt;span class="c"&gt;# ✅ Method 1: Named user&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;groupadd &lt;span class="nt"&gt;-r&lt;/span&gt; appuser &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; useradd &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; appuser appuser
&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; appuser&lt;/span&gt;

&lt;span class="c"&gt;# ✅ Method 2: Numeric UID (more secure)&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;addgroup &lt;span class="nt"&gt;--system&lt;/span&gt; &lt;span class="nt"&gt;--gid&lt;/span&gt; 1001 nodejs &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    adduser &lt;span class="nt"&gt;--system&lt;/span&gt; &lt;span class="nt"&gt;--uid&lt;/span&gt; 1001 nextjs
&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; 1001&lt;/span&gt;

&lt;span class="c"&gt;# ✅ Node.js: Use the built-in 'node' user&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:18-alpine&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm ci &lt;span class="nt"&gt;--only&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;production
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --chown=node:node . .&lt;/span&gt;
&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; node&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 3000&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["node", "server.js"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Kubernetes and AWS ECS policies typically prohibit containers running as root. Always specify a non-root USER. In Kubernetes, set &lt;code&gt;runAsNonRoot: true&lt;/code&gt; in PodSecurityPolicy.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  HEALTHCHECK
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# HTTP health check (common for web apps)&lt;/span&gt;
&lt;span class="k"&gt;HEALTHCHECK&lt;/span&gt;&lt;span class="s"&gt; --interval=30s --timeout=3s --start-period=5s --retries=3 \&lt;/span&gt;
  CMD curl -f http://localhost:3000/health || exit 1

&lt;span class="c"&gt;# Node.js health check (when curl is not available)&lt;/span&gt;
&lt;span class="k"&gt;HEALTHCHECK&lt;/span&gt;&lt;span class="s"&gt; --interval=30s --timeout=5s --start-period=10s --retries=3 \&lt;/span&gt;
  CMD node -e "require('http').get('http://localhost:3000/health', r =&amp;gt; r.statusCode === 200 ? process.exit(0) : process.exit(1))"

# Disable inherited health check
&lt;span class="k"&gt;HEALTHCHECK&lt;/span&gt;&lt;span class="s"&gt; NONE&lt;/span&gt;

&lt;span class="c"&gt;# Options:&lt;/span&gt;
&lt;span class="c"&gt;# --interval=30s    → check every 30 seconds (default: 30s)&lt;/span&gt;
&lt;span class="c"&gt;# --timeout=3s      → fail if no response within 3s (default: 30s)&lt;/span&gt;
&lt;span class="c"&gt;# --start-period=5s → 5s grace period at start (container boot time)&lt;/span&gt;
&lt;span class="c"&gt;# --retries=3       → 3 consecutive failures = unhealthy&lt;/span&gt;

&lt;span class="c"&gt;# Check status&lt;/span&gt;
docker inspect --format='{{.State.Health.Status}}' container_name
docker ps   &lt;span class="c"&gt;# STATUS column shows (healthy) or (unhealthy)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Shell Form vs Exec Form — Signal Handling
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Shell form: runs via /bin/sh -c&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; nginx -g "daemon off;"&lt;/span&gt;
&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; node server.js&lt;/span&gt;

&lt;span class="c"&gt;# Exec form: runs directly (no shell)&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["nginx", "-g", "daemon off;"]&lt;/span&gt;
&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["node", "server.js"]&lt;/span&gt;

&lt;span class="c"&gt;# KEY DIFFERENCE: Signal handling!&lt;/span&gt;
&lt;span class="c"&gt;# Shell form:&lt;/span&gt;
&lt;span class="c"&gt;#   /bin/sh (PID 1) → nginx (child process)&lt;/span&gt;
&lt;span class="c"&gt;#   SIGTERM goes to /bin/sh, not nginx → dirty shutdown!&lt;/span&gt;

&lt;span class="c"&gt;# Exec form:&lt;/span&gt;
&lt;span class="c"&gt;#   nginx (PID 1) directly&lt;/span&gt;
&lt;span class="c"&gt;#   SIGTERM goes directly to nginx → graceful shutdown!&lt;/span&gt;

&lt;span class="c"&gt;# RULE: ALWAYS use exec form in CMD and ENTRYPOINT&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip (Interview):&lt;/strong&gt; "What is the difference between shell form and exec form?" — Shell form runs via &lt;code&gt;/bin/sh -c&lt;/code&gt; (does not become PID 1), exec form executes directly (becomes PID 1). In production, exec form is preferred because signals are properly received.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Writing Efficient Dockerfiles
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Before vs After — 1.5 GB to 180 MB
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Before (1.5 GB image):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; ubuntu:22.04&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; nodejs npm
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; git
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . /app&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 3000&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; node server.js&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Problems: Ubuntu base (~80MB), Node+npm via apt (~200MB), multiple apt layers, all &lt;code&gt;node_modules&lt;/code&gt; included, no cache optimization, running as root, shell form CMD.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;After (180 MB image):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:18-alpine&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm ci &lt;span class="nt"&gt;--only&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;production
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; node&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 3000&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["node", "server.js"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Benefits: Alpine base (~5MB), official Node image, layered cache optimization, production deps only, non-root user, exec CMD form.&lt;/p&gt;

&lt;h3&gt;
  
  
  Layer Ordering — Cache Optimization
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# ❌ BAD ORDER: package.json copied with source code&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:18-alpine&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .                    # Source code AND package.json together&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm ci                  &lt;span class="c"&gt;# Runs on every code change!&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm run build
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 3000&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["node", "server.js"]&lt;/span&gt;

&lt;span class="c"&gt;# ✅ OPTIMAL ORDER: What changes least goes first&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:18-alpine          # Layer 1: base image (almost always cached)&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app                 # Layer 2: almost always cached&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./        # Layer 3: rebuilt only when package.json changes&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm ci                   &lt;span class="c"&gt;# Layer 4: rebuilt only when package.json changes&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .                     # Layer 5: rebuilt on any code change&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm run build            &lt;span class="c"&gt;# Layer 6: rebuilt on any code change&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 3000&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["node", "server.js"]&lt;/span&gt;

&lt;span class="c"&gt;# RESULT:&lt;/span&gt;
&lt;span class="c"&gt;# If only src/App.js changed:&lt;/span&gt;
&lt;span class="c"&gt;# → Layers 1-4: CACHE HIT (npm ci skipped!)&lt;/span&gt;
&lt;span class="c"&gt;# → Layers 5-6: rebuild&lt;/span&gt;
&lt;span class="c"&gt;# Build time: 10s instead of 60s!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Thumb rule: "What changes least goes on top." Order: Base image → System deps → Package manifest (package.json/requirements.txt) → Dependencies install → Source code copy → Build → Configuration.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Base Image Choices
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Base Image&lt;/th&gt;
&lt;th&gt;Size&lt;/th&gt;
&lt;th&gt;Use Case&lt;/th&gt;
&lt;th&gt;Pros/Cons&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ubuntu:22.04&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;~80MB&lt;/td&gt;
&lt;td&gt;Legacy apps needing apt&lt;/td&gt;
&lt;td&gt;Familiar, large&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;debian:slim&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;~75MB&lt;/td&gt;
&lt;td&gt;Debian apps, smaller&lt;/td&gt;
&lt;td&gt;Compatible, slim&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;alpine:3.19&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;~5MB&lt;/td&gt;
&lt;td&gt;Smallest possible&lt;/td&gt;
&lt;td&gt;musl libc issues sometimes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;node:18-alpine&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;~50MB&lt;/td&gt;
&lt;td&gt;Node.js apps&lt;/td&gt;
&lt;td&gt;Best for Node&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;python:3.11-slim&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;~130MB&lt;/td&gt;
&lt;td&gt;Python apps&lt;/td&gt;
&lt;td&gt;Best for Python&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gcr.io/distroless/nodejs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;~30MB&lt;/td&gt;
&lt;td&gt;Security-focused&lt;/td&gt;
&lt;td&gt;No shell — very secure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;scratch&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;0MB&lt;/td&gt;
&lt;td&gt;Static Go/Rust binaries&lt;/td&gt;
&lt;td&gt;Smallest possible&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Security Best Practices
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Pin specific versions&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:18.19.0-alpine3.19   # ✅ Exact version&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:latest                # ❌ Unpredictable&lt;/span&gt;

&lt;span class="c"&gt;# 2. Non-root user (always!)&lt;/span&gt;
&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; node&lt;/span&gt;

&lt;span class="c"&gt;# 3. NEVER put secrets in ENV/ARG permanently&lt;/span&gt;
&lt;span class="c"&gt;# ❌ WRONG — visible in docker history!&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;secret123 &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; ./setup.sh

&lt;span class="c"&gt;# ✅ RIGHT — use runtime env vars&lt;/span&gt;
docker run -e API_KEY=secret123 myapp

&lt;span class="c"&gt;# 4. Don't install unnecessary packages&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="nt"&gt;--no-install-recommends&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    curl &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /var/lib/apt/lists/&lt;span class="k"&gt;*&lt;/span&gt;

&lt;span class="c"&gt;# 5. Read-only filesystem (at runtime)&lt;/span&gt;
docker run --read-only \
    --tmpfs /tmp \
    --tmpfs /app/logs \
    myapp

&lt;span class="c"&gt;# 6. Drop all capabilities (at runtime)&lt;/span&gt;
docker run --cap-drop ALL \
    --cap-add NET_BIND_SERVICE \
    myapp

&lt;span class="c"&gt;# 7. Scan images for vulnerabilities&lt;/span&gt;
docker scout cves myapp:latest
trivy image myapp:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note (Production Security Checklist):&lt;/strong&gt; Non-root USER | &lt;code&gt;.dockerignore&lt;/code&gt; with &lt;code&gt;.env&lt;/code&gt; | No secrets in Dockerfile | Specific base image version | HEALTHCHECK defined | Image vulnerability scan in CI/CD | Read-only filesystem where possible.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  npm install vs npm ci
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Use Case&lt;/th&gt;
&lt;th&gt;In Dockerfile?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm install&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Development, install from package.json&lt;/td&gt;
&lt;td&gt;Avoid&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm ci&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Exact install from package-lock.json&lt;/td&gt;
&lt;td&gt;Always use&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm ci --only=production&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Exclude devDependencies&lt;/td&gt;
&lt;td&gt;For production&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Multi-Stage Builds
&lt;/h2&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;/p&gt;
Multi-stage builds keep the build toolchain in a throw-away stage and ship only the compiled artifact in a minimal final image, cutting image size by 80–90%.





&lt;h3&gt;
  
  
  Why Multi-Stage Builds?
&lt;/h3&gt;

&lt;p&gt;The problem: build tools are needed at build time (compiler, test runner, devDependencies) but should not be in the production image. Without multi-stage builds, everything ends up in the final image — size explodes.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Without Multi-Stage&lt;/th&gt;
&lt;th&gt;With Multi-Stage&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Build tools included (1.2 GB)&lt;/td&gt;
&lt;td&gt;Only production artifacts (120 MB)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;devDependencies included&lt;/td&gt;
&lt;td&gt;Build tools discarded&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Source code included&lt;/td&gt;
&lt;td&gt;Source code discarded&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Security risk (unnecessary tools)&lt;/td&gt;
&lt;td&gt;Minimal attack surface&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Slow to push/pull&lt;/td&gt;
&lt;td&gt;Fast to push/pull&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# BEFORE multi-stage: build tools in final image&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:18&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;      &lt;span class="c"&gt;# devDependencies too — 500MB!&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm run build
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["node", "server.js"]&lt;/span&gt;
&lt;span class="c"&gt;# Final image: ~1.2 GB&lt;/span&gt;

&lt;span class="c"&gt;# AFTER multi-stage: only production artifacts&lt;/span&gt;
&lt;span class="c"&gt;# ---- Stage 1: Builder ----&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;node:18&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;builder&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm ci              &lt;span class="c"&gt;# ALL deps (devDeps for build)&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm run build       &lt;span class="c"&gt;# Create production bundle&lt;/span&gt;

&lt;span class="c"&gt;# ---- Stage 2: Runner (final image) ----&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;node:18-alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;runner&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm ci &lt;span class="nt"&gt;--only&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;production   &lt;span class="c"&gt;# Production deps only&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /app/dist ./dist   # Only built files!&lt;/span&gt;
&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; node&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 3000&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["node", "dist/server.js"]&lt;/span&gt;
&lt;span class="c"&gt;# Final image: ~120 MB  (90% size reduction!)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; &lt;code&gt;COPY --from=builder&lt;/code&gt; only copies the files you specify — not the entire builder stage. Everything else (node_modules, devDeps, source code, compiler) is automatically discarded. The final image contains only the content from the LAST &lt;code&gt;FROM&lt;/code&gt; onwards.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  React App — Production-Ready Multi-Stage
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# ============================================&lt;/span&gt;
&lt;span class="c"&gt;# React App — Production Dockerfile&lt;/span&gt;
&lt;span class="c"&gt;# Multi-stage: Node builder → Nginx server&lt;/span&gt;
&lt;span class="c"&gt;# Final size: ~25-30MB&lt;/span&gt;
&lt;span class="c"&gt;# ============================================&lt;/span&gt;

&lt;span class="c"&gt;# ---- Stage 1: Build ----&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;node:18-alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;builder&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm ci

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;

&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; REACT_APP_API_URL=https://api.myapp.com&lt;/span&gt;
&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; REACT_APP_ENV=production&lt;/span&gt;
&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; REACT_APP_VERSION=1.0.0&lt;/span&gt;

&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; REACT_APP_API_URL=$REACT_APP_API_URL \&lt;/span&gt;
    REACT_APP_ENV=$REACT_APP_ENV \
    REACT_APP_VERSION=$REACT_APP_VERSION

&lt;span class="k"&gt;RUN &lt;/span&gt;npm run build
&lt;span class="c"&gt;# Output: /app/build/&lt;/span&gt;

&lt;span class="c"&gt;# ---- Stage 2: Serve ----&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;nginx:1.25-alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;production&lt;/span&gt;

&lt;span class="k"&gt;LABEL&lt;/span&gt;&lt;span class="s"&gt; maintainer="you@company.com" \&lt;/span&gt;
      version="1.0.0" \
      description="React App Production Image"

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /app/build /usr/share/nginx/html&lt;/span&gt;

&lt;span class="c"&gt;# Custom Nginx config (required for SPA routing!)&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; nginx.conf /etc/nginx/conf.d/default.conf&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; nginx:nginx /usr/share/nginx/html &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nb"&gt;chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; nginx:nginx /var/cache/nginx &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nb"&gt;touch&lt;/span&gt; /var/run/nginx.pid &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nb"&gt;chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; nginx:nginx /var/run/nginx.pid

&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; nginx&lt;/span&gt;

&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 80&lt;/span&gt;

&lt;span class="k"&gt;HEALTHCHECK&lt;/span&gt;&lt;span class="s"&gt; --interval=30s --timeout=3s --start-period=5s --retries=3 \&lt;/span&gt;
  CMD wget -qO- http://localhost/health || exit 1

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["nginx", "-g", "daemon off;"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="c1"&gt;# nginx.conf&lt;/span&gt;
&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;_&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;root&lt;/span&gt; &lt;span class="n"&gt;/usr/share/nginx/html&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;index&lt;/span&gt; &lt;span class="s"&gt;index.html&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;# Handle React Router (SPA routing!)&lt;/span&gt;
    &lt;span class="c1"&gt;# Without this, refreshing /about gives a 404!&lt;/span&gt;
    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;try_files&lt;/span&gt; &lt;span class="nv"&gt;$uri&lt;/span&gt; &lt;span class="nv"&gt;$uri&lt;/span&gt;&lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="n"&gt;/index.html&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;# Long-term cache for static assets&lt;/span&gt;
    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="p"&gt;~&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="s"&gt;.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)&lt;/span&gt;$ &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;expires&lt;/span&gt; &lt;span class="s"&gt;1y&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;add_header&lt;/span&gt; &lt;span class="s"&gt;Cache-Control&lt;/span&gt; &lt;span class="s"&gt;"public,&lt;/span&gt; &lt;span class="s"&gt;immutable"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;# Health check endpoint&lt;/span&gt;
    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/health&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="s"&gt;"healthy&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="s"&gt;n"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;add_header&lt;/span&gt; &lt;span class="s"&gt;Content-Type&lt;/span&gt; &lt;span class="nc"&gt;text/plain&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;# Security headers&lt;/span&gt;
    &lt;span class="kn"&gt;add_header&lt;/span&gt; &lt;span class="s"&gt;X-Frame-Options&lt;/span&gt; &lt;span class="s"&gt;"SAMEORIGIN"&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;add_header&lt;/span&gt; &lt;span class="s"&gt;X-Content-Type-Options&lt;/span&gt; &lt;span class="s"&gt;"nosniff"&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;add_header&lt;/span&gt; &lt;span class="s"&gt;X-XSS-Protection&lt;/span&gt; &lt;span class="s"&gt;"1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="kn"&gt;mode=block"&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;# Gzip compression&lt;/span&gt;
    &lt;span class="kn"&gt;gzip&lt;/span&gt; &lt;span class="no"&gt;on&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;gzip_types&lt;/span&gt; &lt;span class="nc"&gt;text/plain&lt;/span&gt; &lt;span class="nc"&gt;text/css&lt;/span&gt; &lt;span class="nc"&gt;application/json&lt;/span&gt;
               &lt;span class="nc"&gt;application/javascript&lt;/span&gt; &lt;span class="nc"&gt;text/xml&lt;/span&gt; &lt;span class="nc"&gt;application/xml&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;gzip_min_length&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Go Application — Scratch Image (10MB!)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# ---- Stage 1: Go Binary Build ----&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;golang:1.21-alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;builder&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; go.mod go.sum ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;go mod download

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;

&lt;span class="c"&gt;# Static binary build (no external dependencies)&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nv"&gt;CGO_ENABLED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nv"&gt;GOOS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;linux &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nv"&gt;GOARCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;amd64 &lt;span class="se"&gt;\
&lt;/span&gt;    go build &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nt"&gt;-ldflags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"-w -s"&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nt"&gt;-installsuffix&lt;/span&gt; cgo &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nt"&gt;-o&lt;/span&gt; app .

&lt;span class="c"&gt;# ---- Stage 2: Scratch (empty base!) ----&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; scratch&lt;/span&gt;

&lt;span class="c"&gt;# SSL certificates (for HTTPS calls)&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/&lt;/span&gt;

&lt;span class="c"&gt;# Only the binary&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /app/app /app&lt;/span&gt;

&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 8080&lt;/span&gt;

&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["/app"]&lt;/span&gt;

&lt;span class="c"&gt;# Final image: ~8-10MB (just the binary!)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Java Spring Boot Multi-Stage
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# ---- Stage 1: Maven Build ----&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;maven:3.9-openjdk-17-slim&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;builder&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="c"&gt;# Copy pom.xml first (dependency caching!)&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; pom.xml .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;mvn dependency:go-offline &lt;span class="nt"&gt;-B&lt;/span&gt;   &lt;span class="c"&gt;# Download all dependencies&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; src ./src&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;mvn clean package &lt;span class="nt"&gt;-DskipTests&lt;/span&gt; &lt;span class="nt"&gt;-B&lt;/span&gt;

&lt;span class="c"&gt;# ---- Stage 2: JRE Only (no JDK!) ----&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;eclipse-temurin:17-jre-alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;production&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;addgroup &lt;span class="nt"&gt;--system&lt;/span&gt; &lt;span class="nt"&gt;--gid&lt;/span&gt; 1001 spring &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    adduser &lt;span class="nt"&gt;--system&lt;/span&gt; &lt;span class="nt"&gt;--uid&lt;/span&gt; 1001 spring
&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; spring&lt;/span&gt;

&lt;span class="c"&gt;# Only the JAR (not Maven, not JDK)&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /app/target/*.jar app.jar&lt;/span&gt;

&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 8080&lt;/span&gt;

&lt;span class="k"&gt;HEALTHCHECK&lt;/span&gt;&lt;span class="s"&gt; --interval=30s --timeout=10s --start-period=30s --retries=3 \&lt;/span&gt;
  CMD curl -f http://localhost:8080/actuator/health || exit 1

&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["java", \&lt;/span&gt;
  "-XX:+UseContainerSupport", \
  "-XX:MaxRAMPercentage=75.0", \
  "-jar", "app.jar"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Building Specific Stages
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Build up to a specific stage (useful for debugging!)&lt;/span&gt;
docker build &lt;span class="nt"&gt;--target&lt;/span&gt; builder &lt;span class="nt"&gt;-t&lt;/span&gt; myapp:builder &lt;span class="nb"&gt;.&lt;/span&gt;
docker build &lt;span class="nt"&gt;--target&lt;/span&gt; production &lt;span class="nt"&gt;-t&lt;/span&gt; myapp:prod &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="c"&gt;# Open a shell in the builder stage&lt;/span&gt;
docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; myapp:builder sh

&lt;span class="c"&gt;# Separate development vs production builds&lt;/span&gt;
docker build &lt;span class="nt"&gt;--target&lt;/span&gt; development &lt;span class="nt"&gt;-t&lt;/span&gt; myapp:dev &lt;span class="nb"&gt;.&lt;/span&gt;
docker build &lt;span class="nt"&gt;--target&lt;/span&gt; production &lt;span class="nt"&gt;-t&lt;/span&gt; myapp:prod &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Dockerfile Templates Library
&lt;/h2&gt;

&lt;p&gt;These templates are copy-paste ready for production use. All are fully commented and follow security best practices. Customize the &lt;code&gt;EXPOSE&lt;/code&gt; port, user names, and &lt;code&gt;CMD&lt;/code&gt; for your project.&lt;/p&gt;

&lt;h3&gt;
  
  
  Template 1: React App (Production-Ready)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# ============================================&lt;/span&gt;
&lt;span class="c"&gt;# React App — Production Dockerfile&lt;/span&gt;
&lt;span class="c"&gt;# Multi-stage: Node builder → Nginx server&lt;/span&gt;
&lt;span class="c"&gt;# Final size: ~25-30MB&lt;/span&gt;
&lt;span class="c"&gt;# ============================================&lt;/span&gt;

&lt;span class="c"&gt;# ---- Stage 1: Build ----&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;node:18-alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;builder&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm ci

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;

&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; REACT_APP_API_URL=https://api.myapp.com&lt;/span&gt;
&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; REACT_APP_ENV=production&lt;/span&gt;
&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; REACT_APP_VERSION=1.0.0&lt;/span&gt;

&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; REACT_APP_API_URL=$REACT_APP_API_URL \&lt;/span&gt;
    REACT_APP_ENV=$REACT_APP_ENV \
    REACT_APP_VERSION=$REACT_APP_VERSION

&lt;span class="k"&gt;RUN &lt;/span&gt;npm run build
&lt;span class="c"&gt;# Output: /app/build/&lt;/span&gt;

&lt;span class="c"&gt;# ---- Stage 2: Serve ----&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;nginx:1.25-alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;production&lt;/span&gt;

&lt;span class="k"&gt;LABEL&lt;/span&gt;&lt;span class="s"&gt; maintainer="you@company.com" \&lt;/span&gt;
      version="1.0.0" \
      description="React App Production Image"

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /app/build /usr/share/nginx/html&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; nginx.conf /etc/nginx/conf.d/default.conf&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; nginx:nginx /usr/share/nginx/html &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nb"&gt;chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; nginx:nginx /var/cache/nginx &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nb"&gt;touch&lt;/span&gt; /var/run/nginx.pid &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nb"&gt;chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; nginx:nginx /var/run/nginx.pid

&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; nginx&lt;/span&gt;

&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 80&lt;/span&gt;

&lt;span class="k"&gt;HEALTHCHECK&lt;/span&gt;&lt;span class="s"&gt; --interval=30s --timeout=3s --start-period=5s --retries=3 \&lt;/span&gt;
  CMD wget -qO- http://localhost/health || exit 1

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["nginx", "-g", "daemon off;"]&lt;/span&gt;

&lt;span class="c"&gt;# ============================================&lt;/span&gt;
&lt;span class="c"&gt;# Build commands:&lt;/span&gt;
&lt;span class="c"&gt;# docker build -t myreactapp:latest .&lt;/span&gt;
&lt;span class="c"&gt;# docker build --build-arg REACT_APP_API_URL=https://prod.api.com -t myreactapp:prod .&lt;/span&gt;
&lt;span class="c"&gt;# docker run -p 8080:80 myreactapp:latest&lt;/span&gt;
&lt;span class="c"&gt;# ============================================&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Template 2: Node.js Express API
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# ============================================&lt;/span&gt;
&lt;span class="c"&gt;# Node.js Express API — Production Dockerfile&lt;/span&gt;
&lt;span class="c"&gt;# Features: dumb-init, TypeScript support,&lt;/span&gt;
&lt;span class="c"&gt;#           non-root user, health check&lt;/span&gt;
&lt;span class="c"&gt;# Final size: ~80-120MB&lt;/span&gt;
&lt;span class="c"&gt;# ============================================&lt;/span&gt;

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;node:18-alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;base&lt;/span&gt;

&lt;span class="c"&gt;# dumb-init: proper PID 1 handling + signal forwarding&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apk add &lt;span class="nt"&gt;--no-cache&lt;/span&gt; dumb-init

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="c"&gt;# ---- Stage 1: Dependencies ----&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;base&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;deps&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm ci

&lt;span class="c"&gt;# ---- Stage 2: Build (for TypeScript projects) ----&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;deps&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;builder&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm run build 2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"No build step found, skipping..."&lt;/span&gt;

&lt;span class="c"&gt;# ---- Stage 3: Production ----&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;base&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;production&lt;/span&gt;

&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; NODE_ENV=production&lt;/span&gt;

&lt;span class="k"&gt;LABEL&lt;/span&gt;&lt;span class="s"&gt; maintainer="you@company.com" \&lt;/span&gt;
      description="Express API Production Image"

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm ci &lt;span class="nt"&gt;--only&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;production &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    npm cache clean &lt;span class="nt"&gt;--force&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /app/dist ./dist 2&amp;gt;/dev/null || true&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=deps /app/src ./src 2&amp;gt;/dev/null || true&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;addgroup &lt;span class="nt"&gt;--system&lt;/span&gt; &lt;span class="nt"&gt;--gid&lt;/span&gt; 1001 nodejs &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    adduser &lt;span class="nt"&gt;--system&lt;/span&gt; &lt;span class="nt"&gt;--uid&lt;/span&gt; 1001 nodeuser
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; nodeuser:nodejs /app

&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; nodeuser&lt;/span&gt;

&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 3000&lt;/span&gt;

&lt;span class="k"&gt;HEALTHCHECK&lt;/span&gt;&lt;span class="s"&gt; --interval=30s --timeout=3s --start-period=10s --retries=3 \&lt;/span&gt;
  CMD node -e "require('http').get('http://localhost:3000/health', (r) =&amp;gt; { \
    r.statusCode === 200 ? process.exit(0) : process.exit(1); \
  }).on('error', () =&amp;gt; process.exit(1))"

&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["dumb-init", "--"]&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["node", "src/server.js"]&lt;/span&gt;

&lt;span class="c"&gt;# ============================================&lt;/span&gt;
&lt;span class="c"&gt;# Build commands:&lt;/span&gt;
&lt;span class="c"&gt;# docker build -t myapi:latest .&lt;/span&gt;
&lt;span class="c"&gt;# docker run -p 3000:3000 -e DB_URL=mongodb://... myapi:latest&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# For TypeScript projects, change CMD to:&lt;/span&gt;
&lt;span class="c"&gt;# CMD ["node", "dist/server.js"]&lt;/span&gt;
&lt;span class="c"&gt;# ============================================&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Template 3: Python Flask App
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# ============================================&lt;/span&gt;
&lt;span class="c"&gt;# Python Flask App — Production Dockerfile&lt;/span&gt;
&lt;span class="c"&gt;# Features: Gunicorn WSGI, non-root user,&lt;/span&gt;
&lt;span class="c"&gt;#           multi-stage, health check&lt;/span&gt;
&lt;span class="c"&gt;# Final size: ~130-160MB&lt;/span&gt;
&lt;span class="c"&gt;# ============================================&lt;/span&gt;

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;python:3.11-slim&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;base&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    apt-get upgrade &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /var/lib/apt/lists/&lt;span class="k"&gt;*&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;addgroup &lt;span class="nt"&gt;--system&lt;/span&gt; &lt;span class="nt"&gt;--gid&lt;/span&gt; 1001 appgroup &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    adduser &lt;span class="nt"&gt;--system&lt;/span&gt; &lt;span class="nt"&gt;--uid&lt;/span&gt; 1001 &lt;span class="nt"&gt;--gid&lt;/span&gt; 1001 appuser

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="c"&gt;# ---- Stage 2: Dependencies ----&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;base&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;deps&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; requirements.txt .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--no-cache-dir&lt;/span&gt; &lt;span class="nt"&gt;--user&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt

&lt;span class="c"&gt;# ---- Stage 3: Production ----&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;base&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;production&lt;/span&gt;

&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; PYTHONDONTWRITEBYTECODE=1 \&lt;/span&gt;
    PYTHONUNBUFFERED=1 \
    FLASK_ENV=production \
    PORT=5000

&lt;span class="k"&gt;LABEL&lt;/span&gt;&lt;span class="s"&gt; maintainer="you@company.com" \&lt;/span&gt;
      description="Flask API Production Image"

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=deps /root/.local /home/appuser/.local&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; PATH=/home/appuser/.local/bin:$PATH&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --chown=appuser:appgroup . .&lt;/span&gt;

&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; appuser&lt;/span&gt;

&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 5000&lt;/span&gt;

&lt;span class="k"&gt;HEALTHCHECK&lt;/span&gt;&lt;span class="s"&gt; --interval=30s --timeout=5s --start-period=10s --retries=3 \&lt;/span&gt;
  CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:5000/health')" \
      || exit 1

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["python", "-m", "gunicorn", \&lt;/span&gt;
     "--bind", "0.0.0.0:5000", \
     "--workers", "4", \
     "--timeout", "120", \
     "--access-logfile", "-", \
     "--error-logfile", "-", \
     "app:app"]

# ============================================
# requirements.txt must include:
# Flask==3.0.0
&lt;span class="c"&gt;# gunicorn==21.2.0&lt;/span&gt;
&lt;span class="c"&gt;# ============================================&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Template 4: Python FastAPI
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# ============================================&lt;/span&gt;
&lt;span class="c"&gt;# FastAPI App — Production Dockerfile&lt;/span&gt;
&lt;span class="c"&gt;# Features: Uvicorn ASGI, non-root user,&lt;/span&gt;
&lt;span class="c"&gt;#           async support, health check&lt;/span&gt;
&lt;span class="c"&gt;# Final size: ~130-150MB&lt;/span&gt;
&lt;span class="c"&gt;# ============================================&lt;/span&gt;

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;python:3.11-slim&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;base&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="nt"&gt;--no-install-recommends&lt;/span&gt; curl &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /var/lib/apt/lists/&lt;span class="k"&gt;*&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;addgroup &lt;span class="nt"&gt;--system&lt;/span&gt; &lt;span class="nt"&gt;--gid&lt;/span&gt; 1001 appgroup &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    adduser &lt;span class="nt"&gt;--system&lt;/span&gt; &lt;span class="nt"&gt;--uid&lt;/span&gt; 1001 &lt;span class="nt"&gt;--gid&lt;/span&gt; 1001 appuser

&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; PYTHONDONTWRITEBYTECODE=1 \&lt;/span&gt;
    PYTHONUNBUFFERED=1

&lt;span class="k"&gt;LABEL&lt;/span&gt;&lt;span class="s"&gt; maintainer="you@company.com" \&lt;/span&gt;
      description="FastAPI Production Image"

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; requirements.txt .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--no-cache-dir&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --chown=appuser:appgroup . .&lt;/span&gt;

&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; appuser&lt;/span&gt;

&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 8000&lt;/span&gt;

&lt;span class="k"&gt;HEALTHCHECK&lt;/span&gt;&lt;span class="s"&gt; --interval=30s --timeout=3s --start-period=10s --retries=3 \&lt;/span&gt;
  CMD curl -f http://localhost:8000/health || exit 1

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["uvicorn", "main:app", \&lt;/span&gt;
     "--host", "0.0.0.0", \
     "--port", "8000", \
     "--workers", "4", \
     "--loop", "uvloop", \
     "--access-log"]

# ============================================
# requirements.txt must include:
# fastapi==0.109.0
&lt;span class="c"&gt;# uvicorn[standard]==0.27.0&lt;/span&gt;
&lt;span class="c"&gt;# uvloop==0.19.0&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# main.py health endpoint:&lt;/span&gt;
&lt;span class="c"&gt;# @app.get("/health")&lt;/span&gt;
&lt;span class="c"&gt;# async def health(): return {"status": "healthy"}&lt;/span&gt;
&lt;span class="c"&gt;# ============================================&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Template 5: Next.js Production
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# ============================================&lt;/span&gt;
&lt;span class="c"&gt;# Next.js App — Production Dockerfile&lt;/span&gt;
&lt;span class="c"&gt;# Features: Standalone output, optimized,&lt;/span&gt;
&lt;span class="c"&gt;#           non-root, telemetry disabled&lt;/span&gt;
&lt;span class="c"&gt;# Final size: ~100-150MB&lt;/span&gt;
&lt;span class="c"&gt;# ============================================&lt;/span&gt;

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;node:18-alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;base&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apk add &lt;span class="nt"&gt;--no-cache&lt;/span&gt; libc6-compat
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="c"&gt;# ---- Stage 2: Dependencies ----&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;base&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;deps&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm ci

&lt;span class="c"&gt;# ---- Stage 3: Build ----&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;base&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;builder&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=deps /app/node_modules ./node_modules&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;

&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; NEXT_TELEMETRY_DISABLED=1&lt;/span&gt;

&lt;span class="c"&gt;# REQUIRED: next.config.js must have output: 'standalone'&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm run build

&lt;span class="c"&gt;# ---- Stage 4: Runner ----&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;base&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;runner&lt;/span&gt;

&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; NODE_ENV=production&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; NEXT_TELEMETRY_DISABLED=1&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; PORT=3000&lt;/span&gt;

&lt;span class="k"&gt;LABEL&lt;/span&gt;&lt;span class="s"&gt; maintainer="you@company.com" \&lt;/span&gt;
      description="Next.js Production Image"

&lt;span class="k"&gt;RUN &lt;/span&gt;addgroup &lt;span class="nt"&gt;--system&lt;/span&gt; &lt;span class="nt"&gt;--gid&lt;/span&gt; 1001 nodejs &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    adduser &lt;span class="nt"&gt;--system&lt;/span&gt; &lt;span class="nt"&gt;--uid&lt;/span&gt; 1001 nextjs

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /app/public ./public&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder --chown=nextjs:nodejs /app/.next/standalone ./&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static&lt;/span&gt;

&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; nextjs&lt;/span&gt;

&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 3000&lt;/span&gt;

&lt;span class="k"&gt;HEALTHCHECK&lt;/span&gt;&lt;span class="s"&gt; --interval=30s --timeout=3s --start-period=10s --retries=3 \&lt;/span&gt;
  CMD node -e "require('http').get('http://localhost:3000/api/health', r =&amp;gt; \
    r.statusCode === 200 ? process.exit(0) : process.exit(1))"

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["node", "server.js"]&lt;/span&gt;

&lt;span class="c"&gt;# ============================================&lt;/span&gt;
&lt;span class="c"&gt;# REQUIRED: next.config.js must include:&lt;/span&gt;
&lt;span class="c"&gt;# const nextConfig = {&lt;/span&gt;
&lt;span class="c"&gt;#   output: 'standalone',&lt;/span&gt;
&lt;span class="c"&gt;# }&lt;/span&gt;
&lt;span class="c"&gt;# module.exports = nextConfig&lt;/span&gt;
&lt;span class="c"&gt;# ============================================&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; For Next.js Docker images, &lt;code&gt;output: 'standalone'&lt;/code&gt; in &lt;code&gt;next.config.js&lt;/code&gt; is &lt;strong&gt;required&lt;/strong&gt;. Without it, the &lt;code&gt;.next/standalone&lt;/code&gt; folder will not be created and &lt;code&gt;COPY&lt;/code&gt; will fail.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Quick Reference: Which Template to Use
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Project Type&lt;/th&gt;
&lt;th&gt;Template&lt;/th&gt;
&lt;th&gt;Final Size&lt;/th&gt;
&lt;th&gt;Server&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;React (CRA, Vite)&lt;/td&gt;
&lt;td&gt;Template 1&lt;/td&gt;
&lt;td&gt;~25-30MB&lt;/td&gt;
&lt;td&gt;Nginx&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Node.js / Express / Fastify&lt;/td&gt;
&lt;td&gt;Template 2&lt;/td&gt;
&lt;td&gt;~80-120MB&lt;/td&gt;
&lt;td&gt;Node + dumb-init&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Python Flask (sync)&lt;/td&gt;
&lt;td&gt;Template 3&lt;/td&gt;
&lt;td&gt;~130-160MB&lt;/td&gt;
&lt;td&gt;Gunicorn&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Python FastAPI (async)&lt;/td&gt;
&lt;td&gt;Template 4&lt;/td&gt;
&lt;td&gt;~130-150MB&lt;/td&gt;
&lt;td&gt;Uvicorn&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Next.js (full-stack)&lt;/td&gt;
&lt;td&gt;Template 5&lt;/td&gt;
&lt;td&gt;~100-150MB&lt;/td&gt;
&lt;td&gt;Node standalone&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note (Common Patterns in Every Template):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Non-root USER — mandatory for security&lt;/li&gt;
&lt;li&gt;HEALTHCHECK — so Docker/Kubernetes knows container health&lt;/li&gt;
&lt;li&gt;Multi-stage builds — build tools not in final image&lt;/li&gt;
&lt;li&gt;Cache optimization — package.json first, source code second&lt;/li&gt;
&lt;li&gt;Exec form CMD — proper signal handling&lt;/li&gt;
&lt;li&gt;Specific versions — reproducible builds&lt;/li&gt;
&lt;li&gt;LABEL metadata — for traceability&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Networking Fundamentals
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Why Containers Need Networking
&lt;/h3&gt;

&lt;p&gt;By default, every container runs in an isolated environment — its own filesystem, its own process space, and its own network namespace. However, real-world applications require containers to communicate with each other, connect to external databases, and serve clients. Understanding Docker's networking model is therefore essential.&lt;/p&gt;

&lt;p&gt;Scenarios that require networking:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Container-to-Container:&lt;/strong&gt; A frontend container needs to communicate with a backend API&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Container to External Services:&lt;/strong&gt; An application container needs to connect to AWS RDS or a third-party API&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;External Clients to Containers:&lt;/strong&gt; A browser sends a request and a container serves it&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-host Communication:&lt;/strong&gt; Containers on different servers still communicate with each other (Overlay networks)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Container Networking Model (CNM)
&lt;/h3&gt;

&lt;p&gt;Docker defines a standard networking architecture called the &lt;strong&gt;Container Networking Model (CNM)&lt;/strong&gt;. It has three core concepts:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concept&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Real-World Analogy&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Network&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Isolated communication group where containers can talk to each other&lt;/td&gt;
&lt;td&gt;An office floor — only people on that floor can communicate directly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Endpoint&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A virtual NIC (network interface card) for a container on a network&lt;/td&gt;
&lt;td&gt;An employee's desk phone extension&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Sandbox&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Container's isolated network namespace — its own IP, routing table, and DNS&lt;/td&gt;
&lt;td&gt;An employee's complete communication profile&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;strong&gt;libnetwork — Docker's Networking Brain.&lt;/strong&gt; CNM is implemented by &lt;strong&gt;libnetwork&lt;/strong&gt; — Docker's networking subsystem. It is written in Go and supports a pluggable driver architecture. This means you can use bridge, overlay, macvlan — any driver — on top of libnetwork.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Network Driver Architecture:&lt;/strong&gt; libnetwork uses a plugin system. Built-in drivers include: &lt;code&gt;bridge&lt;/code&gt;, &lt;code&gt;host&lt;/code&gt;, &lt;code&gt;none&lt;/code&gt;, &lt;code&gt;overlay&lt;/code&gt;, and &lt;code&gt;macvlan&lt;/code&gt;. Third-party drivers can also be installed (e.g., Weave, Calico).&lt;/p&gt;

&lt;h3&gt;
  
  
  Docker Networking Model — Architecture Overview
&lt;/h3&gt;

&lt;p&gt;Internet traffic arrives at the host → is forwarded to the &lt;code&gt;docker0&lt;/code&gt; bridge → passes through to Container A on port 80 via host port 8080. Container A and Container B can communicate directly with each other using their &lt;code&gt;172.17.x.x&lt;/code&gt; IP addresses through the bridge.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; &lt;strong&gt;veth Pair — The Magic Behind Container Networking.&lt;/strong&gt; When Docker starts a container, it creates a &lt;strong&gt;virtual ethernet pair (veth)&lt;/strong&gt; — like two ends of a virtual cable. One end lives inside the container (&lt;code&gt;eth0&lt;/code&gt;) and the other end attaches to the &lt;code&gt;docker0&lt;/code&gt; bridge. Data flows seamlessly from one end to the other.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Network Types — Bridge, Host, None, Overlay, Macvlan
&lt;/h2&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;
The docker0 bridge connects containers on the same host using private IPs; a published port (-p 8080:80) uses iptables NAT to forward external traffic from the host into a specific container.





&lt;h3&gt;
  
  
  Bridge Network — The Most Common
&lt;/h3&gt;

&lt;p&gt;When you start a container without the &lt;code&gt;--network&lt;/code&gt; flag, it automatically joins the &lt;strong&gt;default bridge network&lt;/strong&gt; (&lt;code&gt;docker0&lt;/code&gt;). However, the default bridge has a significant limitation — &lt;strong&gt;DNS resolution does not work&lt;/strong&gt;!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; &lt;strong&gt;Common Beginner Mistake — No DNS on Default Bridge!&lt;/strong&gt; On the default bridge, containers cannot find each other by name — only by IP address. The IP address can change on every restart. Always use a &lt;strong&gt;user-defined bridge&lt;/strong&gt;!&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Default bridge — no DNS!&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; db mysql:8        &lt;span class="c"&gt;# joins default bridge&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; myapp myimage     &lt;span class="c"&gt;# joins default bridge&lt;/span&gt;
&lt;span class="c"&gt;# myapp CANNOT connect to "db" by hostname!&lt;/span&gt;
&lt;span class="c"&gt;# Must use IP: mysql -h 172.17.0.2 (fragile!)&lt;/span&gt;

&lt;span class="c"&gt;# User-defined bridge — DNS built-in!&lt;/span&gt;
docker network create mynetwork

docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; db &lt;span class="nt"&gt;--network&lt;/span&gt; mynetwork mysql:8
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; myapp &lt;span class="nt"&gt;--network&lt;/span&gt; mynetwork myimage
&lt;span class="c"&gt;# Now myapp can connect directly using the "db" hostname!&lt;/span&gt;
&lt;span class="c"&gt;# MONGODB_URI=mongodb://db:27017/mydb  ← works!&lt;/span&gt;

&lt;span class="c"&gt;# Create a user-defined bridge with options&lt;/span&gt;
docker network create &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--driver&lt;/span&gt; bridge &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--subnet&lt;/span&gt; 172.20.0.0/16 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--gateway&lt;/span&gt; 172.20.0.1 &lt;span class="se"&gt;\&lt;/span&gt;
  my-custom-network
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Default Bridge&lt;/th&gt;
&lt;th&gt;User-Defined Bridge&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;DNS Resolution (by name)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes (container name!)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Automatic Discovery&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Recommended?&lt;/td&gt;
&lt;td&gt;Legacy only&lt;/td&gt;
&lt;td&gt;Always use this&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Network Isolation&lt;/td&gt;
&lt;td&gt;All containers share it&lt;/td&gt;
&lt;td&gt;Per-network isolation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Connect/disconnect from running container&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Host Network — Maximum Performance
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Container shares the host's network namespace&lt;/span&gt;
docker run &lt;span class="nt"&gt;--network&lt;/span&gt; host nginx
&lt;span class="c"&gt;# nginx now listens directly on host port 80&lt;/span&gt;
&lt;span class="c"&gt;# -p flag is not needed (and does not work)&lt;/span&gt;

&lt;span class="c"&gt;# Benefits:&lt;/span&gt;
&lt;span class="c"&gt;# - Best performance: no NAT overhead&lt;/span&gt;
&lt;span class="c"&gt;# - Low latency: direct kernel network stack access&lt;/span&gt;

&lt;span class="c"&gt;# Drawbacks:&lt;/span&gt;
&lt;span class="c"&gt;# - No isolation: container can see all host interfaces&lt;/span&gt;
&lt;span class="c"&gt;# - Port conflicts: container and host services cannot share the same port&lt;/span&gt;

&lt;span class="c"&gt;# Use cases:&lt;/span&gt;
&lt;span class="c"&gt;# - High-performance networking applications&lt;/span&gt;
&lt;span class="c"&gt;# - Monitoring agents (Prometheus node_exporter)&lt;/span&gt;
&lt;span class="c"&gt;# - Network packet inspection tools&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  None Network — Complete Isolation
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# No networking at all — completely isolated&lt;/span&gt;
docker run &lt;span class="nt"&gt;--network&lt;/span&gt; none myapp

&lt;span class="c"&gt;# Inside the container:&lt;/span&gt;
&lt;span class="c"&gt;# - No internet access&lt;/span&gt;
&lt;span class="c"&gt;# - No communication with other containers&lt;/span&gt;
&lt;span class="c"&gt;# - Only loopback (127.0.0.1) is available&lt;/span&gt;

&lt;span class="c"&gt;# Use cases:&lt;/span&gt;
&lt;span class="c"&gt;# - Batch data processing jobs (file input/output only)&lt;/span&gt;
&lt;span class="c"&gt;# - Security-sensitive computations&lt;/span&gt;
&lt;span class="c"&gt;# - Testing network-agnostic code&lt;/span&gt;
&lt;span class="c"&gt;# - Malware analysis environments&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Overlay Network — Multi-Host Communication (Swarm)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Multi-host networking — containers on different servers communicate with each other&lt;/span&gt;
&lt;span class="c"&gt;# Requires: Docker Swarm mode (or Kubernetes)&lt;/span&gt;

&lt;span class="c"&gt;# Initialize Swarm mode&lt;/span&gt;
docker swarm init

&lt;span class="c"&gt;# Create an overlay network&lt;/span&gt;
docker network create &lt;span class="nt"&gt;--driver&lt;/span&gt; overlay myoverlay

&lt;span class="c"&gt;# Attachable overlay (standalone containers can join too)&lt;/span&gt;
docker network create &lt;span class="nt"&gt;--driver&lt;/span&gt; overlay &lt;span class="nt"&gt;--attachable&lt;/span&gt; myoverlay

&lt;span class="c"&gt;# Uses VXLAN tunneling automatically&lt;/span&gt;
&lt;span class="c"&gt;# Containers are unaware they are on different hosts!&lt;/span&gt;
&lt;span class="c"&gt;# 10.0.0.2 (Server 1) &amp;lt;-&amp;gt; 10.0.0.3 (Server 2) — transparent!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;strong&gt;Overlay = VXLAN Tunneling.&lt;/strong&gt; Inside an overlay network, Docker uses &lt;strong&gt;VXLAN (Virtual Extensible LAN)&lt;/strong&gt; — one packet is wrapped inside another. A packet from a container on Server A is wrapped in UDP and sent to Server B, where it is unwrapped. Containers have no idea they are running on different machines.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Macvlan — Container as a Physical Device
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Container gets its own MAC address&lt;/span&gt;
&lt;span class="c"&gt;# Appears on the network like a real physical device&lt;/span&gt;

docker network create &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--driver&lt;/span&gt; macvlan &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--subnet&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;192.168.1.0/24 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--gateway&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;192.168.1.1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;eth0 &lt;span class="se"&gt;\&lt;/span&gt;
  mymacvlan

docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; legacy-app &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--network&lt;/span&gt; mymacvlan &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--ip&lt;/span&gt; 192.168.1.50 &lt;span class="se"&gt;\&lt;/span&gt;
  my-legacy-app

&lt;span class="c"&gt;# The router treats 192.168.1.50 as a physical machine!&lt;/span&gt;

&lt;span class="c"&gt;# Use cases:&lt;/span&gt;
&lt;span class="c"&gt;# - Legacy applications that expect direct network access&lt;/span&gt;
&lt;span class="c"&gt;# - Migrating VMs to containers&lt;/span&gt;
&lt;span class="c"&gt;# - Network monitoring tools&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;strong&gt;Bridge vs Host Network.&lt;/strong&gt; &lt;strong&gt;Bridge:&lt;/strong&gt; Container has its own network namespace, communicates with the host via NAT, and requires port mapping. &lt;strong&gt;Host:&lt;/strong&gt; Container shares the host's network namespace — no NAT, no port mapping, maximum performance. Use Bridge when isolation is required; use Host when performance is critical and isolation can be sacrificed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; &lt;strong&gt;Always Use User-Defined Bridges.&lt;/strong&gt; Never use the default bridge network in production. Create a dedicated user-defined network for each application stack. This provides DNS resolution, isolation, and security all in one. Docker Compose automatically creates a user-defined network for each project.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Network Commands — Complete Reference
&lt;/h2&gt;

&lt;h3&gt;
  
  
  docker network ls — Listing Networks
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# List all networks&lt;/span&gt;
docker network &lt;span class="nb"&gt;ls&lt;/span&gt;
&lt;span class="c"&gt;# NETWORK ID     NAME      DRIVER    SCOPE&lt;/span&gt;
&lt;span class="c"&gt;# a1b2c3d4e5f6   bridge    bridge    local&lt;/span&gt;
&lt;span class="c"&gt;# f6e5d4c3b2a1   host      host      local&lt;/span&gt;
&lt;span class="c"&gt;# 0000000000000   none      null      local&lt;/span&gt;

&lt;span class="c"&gt;# Show full IDs&lt;/span&gt;
docker network &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;--no-trunc&lt;/span&gt;

&lt;span class="c"&gt;# Filter by driver&lt;/span&gt;
docker network &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;--filter&lt;/span&gt; &lt;span class="nv"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;bridge

&lt;span class="c"&gt;# Filter by name&lt;/span&gt;
docker network &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;--filter&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;mynetwork

&lt;span class="c"&gt;# Custom format&lt;/span&gt;
docker network &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;--format&lt;/span&gt; &lt;span class="s2"&gt;"table {{.ID}}&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s2"&gt;{{.Name}}&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s2"&gt;{{.Driver}}&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s2"&gt;{{.Scope}}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  docker network create — Creating a Network
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Simple bridge network&lt;/span&gt;
docker network create mynetwork

&lt;span class="c"&gt;# Bridge with full options&lt;/span&gt;
docker network create &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--driver&lt;/span&gt; bridge &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--subnet&lt;/span&gt; 172.20.0.0/16 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--ip-range&lt;/span&gt; 172.20.240.0/20 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--gateway&lt;/span&gt; 172.20.0.1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--label&lt;/span&gt; &lt;span class="nb"&gt;env&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;production &lt;span class="se"&gt;\&lt;/span&gt;
  mynetwork

&lt;span class="c"&gt;# Internal network (no external access)&lt;/span&gt;
&lt;span class="c"&gt;# Containers cannot connect to the internet&lt;/span&gt;
docker network create &lt;span class="nt"&gt;--internal&lt;/span&gt; private-db-net

&lt;span class="c"&gt;# Overlay network (in Swarm mode)&lt;/span&gt;
docker network create &lt;span class="nt"&gt;--driver&lt;/span&gt; overlay &lt;span class="nt"&gt;--attachable&lt;/span&gt; myoverlay

&lt;span class="c"&gt;# Macvlan network&lt;/span&gt;
docker network create &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--driver&lt;/span&gt; macvlan &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--subnet&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;192.168.1.0/24 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--gateway&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;192.168.1.1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;eth0 &lt;span class="se"&gt;\&lt;/span&gt;
  mymacvlan
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  docker network inspect — Viewing Details
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Full JSON details&lt;/span&gt;
docker network inspect mynetwork

&lt;span class="c"&gt;# Specific field — IPAM config&lt;/span&gt;
docker network inspect &lt;span class="nt"&gt;--format&lt;/span&gt; &lt;span class="s1"&gt;'{{.IPAM.Config}}'&lt;/span&gt; mynetwork

&lt;span class="c"&gt;# Find the gateway&lt;/span&gt;
docker network inspect &lt;span class="nt"&gt;--format&lt;/span&gt; &lt;span class="s1"&gt;'{{range .IPAM.Config}}{{.Gateway}}{{end}}'&lt;/span&gt; mynetwork

&lt;span class="c"&gt;# List connected containers (requires jq)&lt;/span&gt;
docker network inspect mynetwork | jq &lt;span class="s1"&gt;'.[0].Containers'&lt;/span&gt;

&lt;span class="c"&gt;# Most useful: which containers are on this network&lt;/span&gt;
docker network inspect mynetwork &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--format&lt;/span&gt; &lt;span class="s1"&gt;'{{range .Containers}}{{.Name}}: {{.IPv4Address}}{{"\n"}}{{end}}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  docker network connect / disconnect — Runtime Changes
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Connect a running container to a new network&lt;/span&gt;
docker network connect mynetwork mycontainer

&lt;span class="c"&gt;# Assign a specific IP&lt;/span&gt;
docker network connect &lt;span class="nt"&gt;--ip&lt;/span&gt; 172.20.0.100 mynetwork mycontainer

&lt;span class="c"&gt;# Add an alias (other containers can reach this container via the alias)&lt;/span&gt;
docker network connect &lt;span class="nt"&gt;--alias&lt;/span&gt; db-primary mynetwork mycontainer

&lt;span class="c"&gt;# Disconnect (container keeps running)&lt;/span&gt;
docker network disconnect mynetwork mycontainer

&lt;span class="c"&gt;# Force disconnect&lt;/span&gt;
docker network disconnect &lt;span class="nt"&gt;--force&lt;/span&gt; mynetwork mycontainer

&lt;span class="c"&gt;# Real use case: zero-downtime maintenance&lt;/span&gt;
&lt;span class="c"&gt;# 1. Start new container on the new network&lt;/span&gt;
&lt;span class="c"&gt;# 2. Gradually migrate old containers&lt;/span&gt;
&lt;span class="c"&gt;# 3. Disconnect from the old network&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; &lt;strong&gt;Multiple Networks — One Container on Many Networks!&lt;/strong&gt; A container can be connected to more than one network simultaneously. For example, a "gateway" container can be connected to the frontend network on one side and the backend network on the other. This is useful in DMZ architectures where you want to control traffic flow.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  docker network rm / prune — Cleanup
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Remove a network (disconnect all containers first!)&lt;/span&gt;
docker network &lt;span class="nb"&gt;rm &lt;/span&gt;mynetwork
&lt;span class="c"&gt;# Error: network mynetwork has active endpoints — if containers are attached&lt;/span&gt;

&lt;span class="c"&gt;# Remove containers first, then the network&lt;/span&gt;
docker &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;docker ps &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="nt"&gt;--filter&lt;/span&gt; &lt;span class="nv"&gt;network&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;mynetwork&lt;span class="si"&gt;)&lt;/span&gt;
docker network &lt;span class="nb"&gt;rm &lt;/span&gt;mynetwork

&lt;span class="c"&gt;# Remove multiple networks at once&lt;/span&gt;
docker network &lt;span class="nb"&gt;rm &lt;/span&gt;net1 net2 net3

&lt;span class="c"&gt;# Remove all unused networks (none with connected containers)&lt;/span&gt;
docker network prune

&lt;span class="c"&gt;# Skip confirmation prompt&lt;/span&gt;
docker network prune &lt;span class="nt"&gt;-f&lt;/span&gt;

&lt;span class="c"&gt;# Filter by label before pruning&lt;/span&gt;
docker network prune &lt;span class="nt"&gt;--filter&lt;/span&gt; &lt;span class="nv"&gt;label&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;env&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;staging
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; &lt;strong&gt;docker network prune — Be Careful!&lt;/strong&gt; This command deletes all "unused" networks — meaning any network not connected to at least one container. If you created a network for future use, it will be deleted too. Always run &lt;code&gt;docker network ls&lt;/code&gt; before running &lt;code&gt;prune&lt;/code&gt; in production.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Container-to-Container Communication
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Real-World Example: MERN Stack Communication
&lt;/h3&gt;

&lt;p&gt;Consider deploying a MERN (MongoDB, Express, React, Node) application. All three services need to communicate with each other. Here is how to set this up in Docker:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Step 1: Create a dedicated network&lt;/span&gt;
docker network create mern-network

&lt;span class="c"&gt;# Step 2: Start MongoDB first (others depend on it)&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; mongodb &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--network&lt;/span&gt; mern-network &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; mongo_data:/data/db &lt;span class="se"&gt;\&lt;/span&gt;
  mongo:7

&lt;span class="c"&gt;# Step 3: Start the Node.js API&lt;/span&gt;
&lt;span class="c"&gt;# Use "mongodb" as the hostname — Docker DNS resolves it!&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; api &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--network&lt;/span&gt; mern-network &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;MONGODB_URI&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;mongodb://mongodb:27017/mydb &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;PORT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5000 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-p&lt;/span&gt; 5000:5000 &lt;span class="se"&gt;\&lt;/span&gt;
  myapi:latest

&lt;span class="c"&gt;# Step 4: Start the React frontend&lt;/span&gt;
&lt;span class="c"&gt;# Use "api" as the hostname&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; frontend &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--network&lt;/span&gt; mern-network &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;REACT_APP_API_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http://api:5000 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-p&lt;/span&gt; 3000:3000 &lt;span class="se"&gt;\&lt;/span&gt;
  myfrontend:latest

&lt;span class="c"&gt;# Communication flow:&lt;/span&gt;
&lt;span class="c"&gt;# Browser → Host:3000 → frontend container&lt;/span&gt;
&lt;span class="c"&gt;# frontend → api:5000 (DNS resolution via Docker) → api container&lt;/span&gt;
&lt;span class="c"&gt;# api → mongodb:27017 (DNS resolution via Docker) → mongodb container&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Testing DNS Resolution
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Test connectivity&lt;/span&gt;
docker &lt;span class="nb"&gt;exec &lt;/span&gt;frontend ping api &lt;span class="nt"&gt;-c&lt;/span&gt; 4
&lt;span class="c"&gt;# PING api (172.20.0.3): 56 data bytes&lt;/span&gt;
&lt;span class="c"&gt;# 64 bytes from 172.20.0.3: icmp_seq=0 ttl=64 time=0.089 ms&lt;/span&gt;

&lt;span class="c"&gt;# DNS lookup&lt;/span&gt;
docker &lt;span class="nb"&gt;exec &lt;/span&gt;api nslookup mongodb
&lt;span class="c"&gt;# Server:    127.0.0.11  &amp;lt;- Docker's built-in DNS server!&lt;/span&gt;
&lt;span class="c"&gt;# Address 1: 127.0.0.11&lt;/span&gt;
&lt;span class="c"&gt;# Name:      mongodb&lt;/span&gt;
&lt;span class="c"&gt;# Address 1: 172.20.0.2 mongodb.mern-network&lt;/span&gt;

&lt;span class="c"&gt;# Test HTTP connection&lt;/span&gt;
docker &lt;span class="nb"&gt;exec &lt;/span&gt;api wget &lt;span class="nt"&gt;-qO-&lt;/span&gt; http://mongodb:27017
&lt;span class="c"&gt;# It looks like you are trying to access MongoDB over HTTP...&lt;/span&gt;
&lt;span class="c"&gt;# (Connection succeeded — MongoDB responded)&lt;/span&gt;

&lt;span class="c"&gt;# Test the API from the frontend using curl&lt;/span&gt;
docker &lt;span class="nb"&gt;exec &lt;/span&gt;frontend curl http://api:5000/health
&lt;span class="c"&gt;# {"status":"ok","db":"connected"}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;strong&gt;127.0.0.11 — Docker's Secret DNS Server.&lt;/strong&gt; Whenever you use a user-defined network, Docker provides an embedded DNS server at &lt;strong&gt;127.0.0.11&lt;/strong&gt; inside every container. It resolves container names to IP addresses. This happens automatically — no configuration required.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Service Discovery Patterns — Best Practices
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pattern&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;th&gt;When to Use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Container Name as Hostname&lt;/td&gt;
&lt;td&gt;&lt;code&gt;mongodb://mongodb:27017&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Simple setups, same host&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Docker Compose Service Name&lt;/td&gt;
&lt;td&gt;&lt;code&gt;http://api:5000&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Compose-based deployments&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Network Alias&lt;/td&gt;
&lt;td&gt;&lt;code&gt;--network-alias db&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;When the container name may change&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Environment Variables&lt;/td&gt;
&lt;td&gt;&lt;code&gt;DB_HOST=mongodb&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Externalizing configuration&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; &lt;strong&gt;Never Hardcode IPs!&lt;/strong&gt; A very common mistake: &lt;code&gt;MONGODB_URI=mongodb://172.17.0.2:27017&lt;/code&gt;. The IP can change when the container restarts. Always use the container name as the hostname: &lt;code&gt;mongodb://mongodb:27017&lt;/code&gt;. The container name is stable; the IP is not.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;strong&gt;Production Pattern: Network Segmentation.&lt;/strong&gt; In real production environments, create separate networks: &lt;code&gt;frontend-net&lt;/code&gt; (frontend + API) and &lt;code&gt;backend-net&lt;/code&gt; (API + database). The database will not be directly accessible from the frontend — only through the API. This is the Zero Trust networking principle. In Docker Compose, this is easily configured with the &lt;code&gt;networks:&lt;/code&gt; key.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Port Management — EXPOSE vs -p vs -P
&lt;/h2&gt;

&lt;h3&gt;
  
  
  EXPOSE vs -p — Clearing the Confusion
&lt;/h3&gt;

&lt;p&gt;This is a classic point of confusion for beginners. Both &lt;code&gt;EXPOSE&lt;/code&gt; and &lt;code&gt;-p&lt;/code&gt; deal with ports, but they serve different purposes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# EXPOSE in Dockerfile — documentation only!&lt;/span&gt;
EXPOSE 3000
&lt;span class="c"&gt;# This only says "this container listens on port 3000"&lt;/span&gt;
&lt;span class="c"&gt;# It does NOT publish anything to the HOST&lt;/span&gt;
&lt;span class="c"&gt;# Other containers can read this information via inspect&lt;/span&gt;

&lt;span class="c"&gt;# -p flag: ACTUALLY publishes the port on the host&lt;/span&gt;
docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:3000 myapp
&lt;span class="c"&gt;# host:8080 → container:3000&lt;/span&gt;
&lt;span class="c"&gt;# Requests arriving at host port 8080 are forwarded to container port 3000&lt;/span&gt;

&lt;span class="c"&gt;# -P flag (capital P): publish ALL EXPOSEd ports to random host ports&lt;/span&gt;
docker run &lt;span class="nt"&gt;-P&lt;/span&gt; myapp
&lt;span class="c"&gt;# If Dockerfile has EXPOSE 3000 3306:&lt;/span&gt;
&lt;span class="c"&gt;# Docker allocates: 32768:3000, 32769:3306 (random!)&lt;/span&gt;

&lt;span class="c"&gt;# Check which port was assigned&lt;/span&gt;
docker port myapp
&lt;span class="c"&gt;# 3000/tcp -&amp;gt; 0.0.0.0:32768&lt;/span&gt;

docker port myapp 3000
&lt;span class="c"&gt;# 0.0.0.0:32768&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; &lt;strong&gt;Mnemonic: EXPOSE = Advertisement, -p = Actually Open the Door.&lt;/strong&gt; EXPOSE is a "menu card" — it only describes what is available. The &lt;code&gt;-p&lt;/code&gt; flag actually opens the door to the outside world. Without &lt;code&gt;-p&lt;/code&gt;, services inside the container are not accessible from the host (though other containers on a custom network can still access them without port mapping).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Port Mapping Patterns
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Specific mapping (most common)&lt;/span&gt;
docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:80 nginx          &lt;span class="c"&gt;# Host 8080 → Container 80&lt;/span&gt;

&lt;span class="c"&gt;# 2. Multiple ports&lt;/span&gt;
docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 80:80 &lt;span class="nt"&gt;-p&lt;/span&gt; 443:443 nginx

&lt;span class="c"&gt;# 3. Default binding: 0.0.0.0 (all interfaces)&lt;/span&gt;
docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:80 nginx
&lt;span class="c"&gt;# Any IP can reach port 8080 and hit the container&lt;/span&gt;
&lt;span class="c"&gt;# curl localhost:8080 works&lt;/span&gt;
&lt;span class="c"&gt;# curl 192.168.1.100:8080 works (accessible on LAN too)&lt;/span&gt;

&lt;span class="c"&gt;# 4. Bind to localhost only (security)&lt;/span&gt;
docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 127.0.0.1:8080:80 nginx
&lt;span class="c"&gt;# Only accessible from the same machine&lt;/span&gt;
&lt;span class="c"&gt;# curl localhost:8080 works&lt;/span&gt;
&lt;span class="c"&gt;# curl 192.168.1.100:8080 does not work&lt;/span&gt;

&lt;span class="c"&gt;# 5. Random port on all interfaces&lt;/span&gt;
docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 0.0.0.0::80 nginx
&lt;span class="c"&gt;# Docker chooses a random high port&lt;/span&gt;
docker port &amp;lt;container&amp;gt; 80
&lt;span class="c"&gt;# 0.0.0.0:49153&lt;/span&gt;

&lt;span class="c"&gt;# 6. UDP port&lt;/span&gt;
docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 53:53/udp my-dns-server

&lt;span class="c"&gt;# 7. Range of ports&lt;/span&gt;
docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 8000-8010:8000-8010 myapp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Handling Port Conflicts
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Error: Bind: address already in use&lt;/span&gt;
&lt;span class="c"&gt;# docker: Error response from daemon: driver failed programming&lt;/span&gt;
&lt;span class="c"&gt;# external connectivity on endpoint ...: Bind for 0.0.0.0:8080&lt;/span&gt;
&lt;span class="c"&gt;# failed: port is already allocated&lt;/span&gt;

&lt;span class="c"&gt;# Step 1: Find out who is using port 8080&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;lsof &lt;span class="nt"&gt;-i&lt;/span&gt; :8080
&lt;span class="nb"&gt;sudo &lt;/span&gt;netstat &lt;span class="nt"&gt;-tulpn&lt;/span&gt; | &lt;span class="nb"&gt;grep &lt;/span&gt;8080

&lt;span class="c"&gt;# Step 2: Options:&lt;/span&gt;
&lt;span class="c"&gt;# Option A: Use a different host port&lt;/span&gt;
docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 9090:80 nginx

&lt;span class="c"&gt;# Option B: Stop the conflicting process&lt;/span&gt;
&lt;span class="nb"&gt;sudo kill&lt;/span&gt; &lt;span class="nt"&gt;-9&lt;/span&gt; &amp;lt;PID&amp;gt;

&lt;span class="c"&gt;# Option C: Check if another Docker container is using the port&lt;/span&gt;
docker ps &lt;span class="nt"&gt;--format&lt;/span&gt; &lt;span class="s2"&gt;"table {{.Names}}&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s2"&gt;{{.Ports}}"&lt;/span&gt; | &lt;span class="nb"&gt;grep &lt;/span&gt;8080
docker stop old-nginx
docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:80 nginx

&lt;span class="c"&gt;# Useful: view all mapped ports&lt;/span&gt;
docker ps &lt;span class="nt"&gt;--format&lt;/span&gt; &lt;span class="s2"&gt;"table {{.ID}}&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s2"&gt;{{.Names}}&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s2"&gt;{{.Ports}}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; &lt;strong&gt;Security: Bind to Localhost in Development.&lt;/strong&gt; When you use &lt;code&gt;-p 8080:80&lt;/code&gt;, Docker automatically adds an iptables rule — even if UFW or firewalld is disabled. This means the service may be accessible from the internet. In development, always use &lt;code&gt;-p 127.0.0.1:8080:80&lt;/code&gt; for sensitive services.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;strong&gt;EXPOSE vs -p — Interview Answer.&lt;/strong&gt; &lt;code&gt;EXPOSE&lt;/code&gt; is only metadata/documentation — it does not actually publish any port. The &lt;code&gt;-p&lt;/code&gt; flag creates an actual port mapping at runtime between the host and container using iptables rules. EXPOSE without &lt;code&gt;-p&lt;/code&gt; is useless for host access (though it does provide information to other containers within the network).&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Storage Fundamentals — Why Persistence Matters
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Containers Are Ephemeral — Data Is Lost!
&lt;/h3&gt;

&lt;p&gt;The most important concept in storage: &lt;strong&gt;containers are ephemeral (temporary) by default&lt;/strong&gt;. When a container is deleted, all its data is deleted too.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; &lt;strong&gt;"The container was deleted and the data went with it!" — Classic Production Disaster.&lt;/strong&gt; Imagine you started a MySQL container, added some data, and then accidentally ran &lt;code&gt;docker rm -f mysql&lt;/code&gt;. The entire database is gone. This happens frequently in production. Solution: always use volumes for databases.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Container storage layers use a union filesystem (overlay2):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────────────────────────┐
│  [Writable Layer]  ← Temporary!         │
│  The container's write area             │
│  Container deleted → THIS DATA IS GONE! │
├─────────────────────────────────────────┤
│  [App Layer]       ← Read-only          │
│  Your application code                  │
├─────────────────────────────────────────┤
│  [Base Image Layer] ← Read-only         │
│  ubuntu:22.04 or node:18 etc.           │
└─────────────────────────────────────────┘

# The bottom layers are part of the image — shared across containers!
# Only the writable layer is container-specific — and it is temporary
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Storage Drivers — The Underlying Filesystem
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Driver&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;When to Use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;overlay2&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Default; uses Linux overlayfs&lt;/td&gt;
&lt;td&gt;Almost always — best choice&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;devicemapper&lt;/td&gt;
&lt;td&gt;Block device based&lt;/td&gt;
&lt;td&gt;Legacy RHEL/CentOS (outdated)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;btrfs&lt;/td&gt;
&lt;td&gt;Btrfs filesystem&lt;/td&gt;
&lt;td&gt;Only on Btrfs hosts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;zfs&lt;/td&gt;
&lt;td&gt;ZFS filesystem&lt;/td&gt;
&lt;td&gt;Only on ZFS hosts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;vfs&lt;/td&gt;
&lt;td&gt;No layering, simple copy&lt;/td&gt;
&lt;td&gt;Testing only — very slow&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check the current storage driver&lt;/span&gt;
docker info | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"Storage Driver"&lt;/span&gt;
&lt;span class="c"&gt;# Storage Driver: overlay2&lt;/span&gt;

&lt;span class="c"&gt;# Where does Docker store data?&lt;/span&gt;
/var/lib/docker/
  ├── containers/     &lt;span class="c"&gt;# Per-container metadata, logs&lt;/span&gt;
  ├── image/          &lt;span class="c"&gt;# Image metadata and manifests&lt;/span&gt;
  ├── overlay2/       &lt;span class="c"&gt;# Image layers (most data is here!)&lt;/span&gt;
  ├── volumes/        &lt;span class="c"&gt;# Named volumes&lt;/span&gt;
  └── network/        &lt;span class="c"&gt;# Network configs&lt;/span&gt;

&lt;span class="c"&gt;# Check sizes&lt;/span&gt;
&lt;span class="nb"&gt;sudo du&lt;/span&gt; &lt;span class="nt"&gt;-sh&lt;/span&gt; /var/lib/docker/&lt;span class="k"&gt;*&lt;/span&gt;
&lt;span class="c"&gt;# 1.2G    /var/lib/docker/overlay2   # Most data here!&lt;/span&gt;
&lt;span class="c"&gt;# 200M    /var/lib/docker/volumes&lt;/span&gt;
&lt;span class="c"&gt;# 50M     /var/lib/docker/containers&lt;/span&gt;

&lt;span class="c"&gt;# Docker summary&lt;/span&gt;
docker system &lt;span class="nb"&gt;df&lt;/span&gt;
&lt;span class="c"&gt;# TYPE            TOTAL     ACTIVE    SIZE      RECLAIMABLE&lt;/span&gt;
&lt;span class="c"&gt;# Images          15        5         3.2GB     2.1GB (65%)&lt;/span&gt;
&lt;span class="c"&gt;# Containers      8         3         120MB     90MB (75%)&lt;/span&gt;
&lt;span class="c"&gt;# Local Volumes   5         2         1.1GB     500MB (45%)&lt;/span&gt;

&lt;span class="c"&gt;# Detailed breakdown&lt;/span&gt;
docker system &lt;span class="nb"&gt;df&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; &lt;strong&gt;Copy-on-Write (CoW) — The Secret of Efficiency.&lt;/strong&gt; overlay2 uses a &lt;strong&gt;Copy-on-Write&lt;/strong&gt; strategy. If 10 containers use the same base image, the image layers are stored only once on disk. Only when a container writes something does it create its own copy of that file. This saves both disk space and startup time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;strong&gt;Production Rule: Databases Always Need Volumes!&lt;/strong&gt; MySQL, PostgreSQL, MongoDB, Redis — whenever you run any database in a container, volumes are MANDATORY. Without a volume, all data is permanently lost when the container crashes or restarts. This is the number one cause of production disasters.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Volume Types — Named, Bind Mount, tmpfs
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Named Volumes — Production's Best Friend
&lt;/h3&gt;

&lt;p&gt;Named volumes are Docker's &lt;strong&gt;managed volumes&lt;/strong&gt; — Docker decides where to store the data (&lt;code&gt;/var/lib/docker/volumes/&lt;/code&gt;). They survive container restarts and deletions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create a volume&lt;/span&gt;
docker volume create mydata

&lt;span class="c"&gt;# Use with -v syntax&lt;/span&gt;
docker run &lt;span class="nt"&gt;-v&lt;/span&gt; mydata:/app/data myapp

&lt;span class="c"&gt;# Use with --mount syntax (recommended)&lt;/span&gt;
docker run &lt;span class="nt"&gt;--mount&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;volume,source&lt;span class="o"&gt;=&lt;/span&gt;mydata,target&lt;span class="o"&gt;=&lt;/span&gt;/app/data myapp

&lt;span class="c"&gt;# Use with a database&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; postgres &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; postgres_data:/var/lib/postgresql/data &lt;span class="se"&gt;\&lt;/span&gt;
  postgres:16

&lt;span class="c"&gt;# Delete the container — THE VOLUME STAYS SAFE!&lt;/span&gt;
docker &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; postgres
docker volume &lt;span class="nb"&gt;ls&lt;/span&gt;
&lt;span class="c"&gt;# postgres_data  ← Still there!&lt;/span&gt;

&lt;span class="c"&gt;# Start a new container with the same volume — data is back!&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; postgres-new &lt;span class="nt"&gt;-v&lt;/span&gt; postgres_data:/var/lib/postgresql/data postgres:16
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Bind Mounts — Development's Superpower
&lt;/h3&gt;

&lt;p&gt;With bind mounts, you specify the host path explicitly. This is extremely useful in development because your local changes are instantly reflected inside the container — &lt;strong&gt;without rebuilding&lt;/strong&gt;!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Specify the host path explicitly&lt;/span&gt;
docker run &lt;span class="nt"&gt;-v&lt;/span&gt; /home/pushkar/myproject:/app node:18

&lt;span class="c"&gt;# Mount the current directory (most common in development!)&lt;/span&gt;
docker run &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;:/app node:18
&lt;span class="c"&gt;# Edit code locally — changes appear inside the container immediately!&lt;/span&gt;
&lt;span class="c"&gt;# Hot reload works!&lt;/span&gt;

&lt;span class="c"&gt;# Node.js development example&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; dev-server &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;:/app &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-w&lt;/span&gt; /app &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-p&lt;/span&gt; 3000:3000 &lt;span class="se"&gt;\&lt;/span&gt;
  node:18 &lt;span class="se"&gt;\&lt;/span&gt;
  npm run dev

&lt;span class="c"&gt;# Read-only bind mount (for security — container cannot modify)&lt;/span&gt;
docker run &lt;span class="nt"&gt;-v&lt;/span&gt; /etc/nginx/conf:/etc/nginx/conf:ro nginx

&lt;span class="c"&gt;# --mount syntax&lt;/span&gt;
docker run &lt;span class="nt"&gt;--mount&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;bind&lt;/span&gt;,source&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;,target&lt;span class="o"&gt;=&lt;/span&gt;/app node:18
docker run &lt;span class="nt"&gt;--mount&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;bind&lt;/span&gt;,source&lt;span class="o"&gt;=&lt;/span&gt;/config,target&lt;span class="o"&gt;=&lt;/span&gt;/config,readonly nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; &lt;strong&gt;For React Developers: Bind Mount = Live Reload!&lt;/strong&gt; &lt;code&gt;docker run -v $(pwd):/app -p 3000:3000 node:18 npm start&lt;/code&gt; — edit code in VS Code and React hot reload fires automatically inside the container. No rebuild needed for each change. This is a game-changer for development workflow.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  tmpfs Mounts — Sensitive Data In-Memory
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Stored in memory, never written to disk!&lt;/span&gt;
docker run &lt;span class="nt"&gt;--tmpfs&lt;/span&gt; /tmp nginx

&lt;span class="c"&gt;# With a size limit&lt;/span&gt;
docker run &lt;span class="nt"&gt;--mount&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;tmpfs,target&lt;span class="o"&gt;=&lt;/span&gt;/tmp,tmpfs-size&lt;span class="o"&gt;=&lt;/span&gt;100m myapp

&lt;span class="c"&gt;# Multiple tmpfs locations&lt;/span&gt;
docker run &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--tmpfs&lt;/span&gt; /tmp &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--tmpfs&lt;/span&gt; /run &lt;span class="se"&gt;\&lt;/span&gt;
  myapp

&lt;span class="c"&gt;# Use cases:&lt;/span&gt;
&lt;span class="c"&gt;# - Session data (passwords, tokens stored temporarily)&lt;/span&gt;
&lt;span class="c"&gt;# - Sensitive computation results&lt;/span&gt;
&lt;span class="c"&gt;# - Cache data that should never touch disk&lt;/span&gt;
&lt;span class="c"&gt;# - High-speed temporary storage&lt;/span&gt;

&lt;span class="c"&gt;# Container stops → data gone immediately (from RAM)&lt;/span&gt;
&lt;span class="c"&gt;# Container crashes → data gone immediately&lt;/span&gt;
&lt;span class="c"&gt;# Never persists to disk = more secure&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Comparison — Which Type to Use When?
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Named Volume&lt;/th&gt;
&lt;th&gt;Bind Mount&lt;/th&gt;
&lt;th&gt;tmpfs&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Storage Location&lt;/td&gt;
&lt;td&gt;/var/lib/docker/volumes&lt;/td&gt;
&lt;td&gt;Any host path&lt;/td&gt;
&lt;td&gt;Memory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Docker Managed&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data Persists&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No (memory only)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dev Workflow&lt;/td&gt;
&lt;td&gt;OK&lt;/td&gt;
&lt;td&gt;Best (hot reload!)&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Production DB&lt;/td&gt;
&lt;td&gt;Best&lt;/td&gt;
&lt;td&gt;OK (path management)&lt;/td&gt;
&lt;td&gt;Never&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Easy Backup&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Performance&lt;/td&gt;
&lt;td&gt;Good&lt;/td&gt;
&lt;td&gt;Best (NFS will be slow)&lt;/td&gt;
&lt;td&gt;Excellent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Security&lt;/td&gt;
&lt;td&gt;Container isolated&lt;/td&gt;
&lt;td&gt;Host exposed&lt;/td&gt;
&lt;td&gt;Most secure&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;strong&gt;Named Volume vs Bind Mount — Interview Answer.&lt;/strong&gt; &lt;strong&gt;Named Volume:&lt;/strong&gt; Docker manages the location; it is portable, easy to back up, and best for production. &lt;strong&gt;Bind Mount:&lt;/strong&gt; You control the specific host path; perfect for development (host files accessible directly in container), but path is host-specific — less portable. Prefer named volumes in production; use bind mounts in development for code changes.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Volume Commands — Complete Reference
&lt;/h2&gt;

&lt;h3&gt;
  
  
  docker volume create — Creating Volumes
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Simple named volume&lt;/span&gt;
docker volume create mydb-data

&lt;span class="c"&gt;# Explicitly use the local driver (this is already the default)&lt;/span&gt;
docker volume create &lt;span class="nt"&gt;--driver&lt;/span&gt; &lt;span class="nb"&gt;local &lt;/span&gt;mydb-data

&lt;span class="c"&gt;# NFS-backed volume (remote storage!)&lt;/span&gt;
docker volume create &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--driver&lt;/span&gt; &lt;span class="nb"&gt;local&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--opt&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;nfs &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--opt&lt;/span&gt; &lt;span class="nv"&gt;o&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;addr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;192.168.1.100,rw,vers&lt;span class="o"&gt;=&lt;/span&gt;4 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--opt&lt;/span&gt; &lt;span class="nv"&gt;device&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;:/exports/mydata &lt;span class="se"&gt;\&lt;/span&gt;
  nfs-volume

&lt;span class="c"&gt;# With labels (for management)&lt;/span&gt;
docker volume create &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--label&lt;/span&gt; &lt;span class="nb"&gt;env&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;production &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--label&lt;/span&gt; &lt;span class="nv"&gt;app&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;myapp &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--label&lt;/span&gt; &lt;span class="nv"&gt;team&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;backend &lt;span class="se"&gt;\&lt;/span&gt;
  mydb-data

&lt;span class="c"&gt;# Volumes are automatically created if referenced in a run command&lt;/span&gt;
docker run &lt;span class="nt"&gt;-v&lt;/span&gt; newvolume:/data myapp
&lt;span class="c"&gt;# "newvolume" is created automatically if it does not exist&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  docker volume ls, inspect, rm, prune
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# List all volumes&lt;/span&gt;
docker volume &lt;span class="nb"&gt;ls&lt;/span&gt;
&lt;span class="c"&gt;# DRIVER    VOLUME NAME&lt;/span&gt;
&lt;span class="c"&gt;# local     mydb-data&lt;/span&gt;
&lt;span class="c"&gt;# local     postgres_data&lt;/span&gt;
&lt;span class="c"&gt;# local     mongo_data&lt;/span&gt;

&lt;span class="c"&gt;# Find dangling volumes (not connected to any container)&lt;/span&gt;
docker volume &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="nv"&gt;dangling&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;

&lt;span class="c"&gt;# Filter by label&lt;/span&gt;
docker volume &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;--filter&lt;/span&gt; &lt;span class="nv"&gt;label&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;env&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;production

&lt;span class="c"&gt;# Inspect — full details&lt;/span&gt;
docker volume inspect mydb-data
&lt;span class="c"&gt;# [&lt;/span&gt;
&lt;span class="c"&gt;#   {&lt;/span&gt;
&lt;span class="c"&gt;#     "Name": "mydb-data",&lt;/span&gt;
&lt;span class="c"&gt;#     "Driver": "local",&lt;/span&gt;
&lt;span class="c"&gt;#     "Mountpoint": "/var/lib/docker/volumes/mydb-data/_data",&lt;/span&gt;
&lt;span class="c"&gt;#     "Labels": {"env": "production"},&lt;/span&gt;
&lt;span class="c"&gt;#     ...&lt;/span&gt;
&lt;span class="c"&gt;#   }&lt;/span&gt;
&lt;span class="c"&gt;# ]&lt;/span&gt;

&lt;span class="c"&gt;# Get only the mount path&lt;/span&gt;
docker volume inspect &lt;span class="nt"&gt;--format&lt;/span&gt; &lt;span class="s1"&gt;'{{.Mountpoint}}'&lt;/span&gt; mydb-data
&lt;span class="c"&gt;# /var/lib/docker/volumes/mydb-data/_data&lt;/span&gt;

&lt;span class="c"&gt;# Remove — stop the container first!&lt;/span&gt;
docker volume &lt;span class="nb"&gt;rm &lt;/span&gt;mydb-data
&lt;span class="c"&gt;# Error: volume is in use — if a container is running&lt;/span&gt;

&lt;span class="c"&gt;# Correct approach: remove the container first&lt;/span&gt;
docker &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; mycontainer
docker volume &lt;span class="nb"&gt;rm &lt;/span&gt;mydb-data

&lt;span class="c"&gt;# Remove multiple at once&lt;/span&gt;
&lt;span class="c"&gt;# (Remove containers first, then volumes)&lt;/span&gt;
docker volume prune

&lt;span class="c"&gt;# Skip confirmation&lt;/span&gt;
docker volume prune &lt;span class="nt"&gt;-f&lt;/span&gt;

&lt;span class="c"&gt;# Filter by label before pruning&lt;/span&gt;
docker volume prune &lt;span class="nt"&gt;--filter&lt;/span&gt; &lt;span class="nv"&gt;label&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;env&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;staging
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  --mount vs -v Syntax — The Modern Way
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# ─── OLD -v SYNTAX ──────────────────────────────────────&lt;/span&gt;
&lt;span class="c"&gt;# Older style, still works, but less readable&lt;/span&gt;

&lt;span class="c"&gt;# Named volume&lt;/span&gt;
docker run &lt;span class="nt"&gt;-v&lt;/span&gt; myvolume:/app/data myapp

&lt;span class="c"&gt;# Bind mount&lt;/span&gt;
docker run &lt;span class="nt"&gt;-v&lt;/span&gt; /host/path:/container/path myapp

&lt;span class="c"&gt;# Read-only bind mount&lt;/span&gt;
docker run &lt;span class="nt"&gt;-v&lt;/span&gt; /host/path:/container/path:ro myapp

&lt;span class="c"&gt;# ─── NEW --mount SYNTAX ──────────────────────────────────&lt;/span&gt;
&lt;span class="c"&gt;# Newer, explicit, recommended&lt;/span&gt;

&lt;span class="c"&gt;# Named volume&lt;/span&gt;
docker run &lt;span class="nt"&gt;--mount&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;volume,source&lt;span class="o"&gt;=&lt;/span&gt;myvolume,target&lt;span class="o"&gt;=&lt;/span&gt;/app/data myapp

&lt;span class="c"&gt;# Bind mount&lt;/span&gt;
docker run &lt;span class="nt"&gt;--mount&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;bind&lt;/span&gt;,source&lt;span class="o"&gt;=&lt;/span&gt;/host/path,target&lt;span class="o"&gt;=&lt;/span&gt;/app/path myapp

&lt;span class="c"&gt;# Read-only bind mount&lt;/span&gt;
docker run &lt;span class="nt"&gt;--mount&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;bind&lt;/span&gt;,source&lt;span class="o"&gt;=&lt;/span&gt;/host/path,target&lt;span class="o"&gt;=&lt;/span&gt;/app/path,readonly myapp

&lt;span class="c"&gt;# tmpfs&lt;/span&gt;
docker run &lt;span class="nt"&gt;--mount&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;tmpfs,target&lt;span class="o"&gt;=&lt;/span&gt;/tmp,tmpfs-size&lt;span class="o"&gt;=&lt;/span&gt;100m myapp

&lt;span class="c"&gt;# Why --mount is better:&lt;/span&gt;
&lt;span class="c"&gt;# - More explicit — type is clearly stated&lt;/span&gt;
&lt;span class="c"&gt;# - Better error messages&lt;/span&gt;
&lt;span class="c"&gt;# - Supports all options (tmpfs-size, etc.)&lt;/span&gt;
&lt;span class="c"&gt;# - Recommended format in Docker Docs&lt;/span&gt;
&lt;span class="c"&gt;# - More readable in scripts&lt;/span&gt;
&lt;span class="c"&gt;# - Named parameters catch typos&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;strong&gt;-v vs --mount: One Important Difference.&lt;/strong&gt; A subtle distinction: &lt;code&gt;-v /nonexistent:/data&lt;/code&gt; will cause Docker to automatically create the host directory (sometimes with the wrong type). &lt;code&gt;--mount type=bind,source=/nonexistent,target=/data&lt;/code&gt; returns an error if the source does not exist. &lt;strong&gt;--mount is stricter and more predictable&lt;/strong&gt; — which makes it better for production.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; &lt;strong&gt;docker volume prune — Be Very Careful in Production!&lt;/strong&gt; This command deletes "dangling" (unused) volumes. However, if you have temporarily stopped a container (intending to restore its data), its volume may be deleted too. In production, first run &lt;code&gt;docker volume ls -f dangling=true&lt;/code&gt; to see which volumes are actually unused, then selectively use &lt;code&gt;docker volume rm&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Production Volume Patterns — Real-World Usage
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Database Persistence Patterns
&lt;/h3&gt;

&lt;h4&gt;
  
  
  PostgreSQL with Named Volume
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Step 1: Create the volume&lt;/span&gt;
docker volume create postgres_data

&lt;span class="c"&gt;# Step 2: Run PostgreSQL with a proper production setup&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; postgres &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;POSTGRES_USER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;admin &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;securepassword123 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;POSTGRES_DB&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;myapp &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; postgres_data:/var/lib/postgresql/data &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-p&lt;/span&gt; 127.0.0.1:5432:5432 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--restart&lt;/span&gt; unless-stopped &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--health-cmd&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"pg_isready -U admin"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--health-interval&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;10s &lt;span class="se"&gt;\&lt;/span&gt;
  postgres:16-alpine

&lt;span class="c"&gt;# Verify&lt;/span&gt;
docker ps                                         &lt;span class="c"&gt;# Check status and health&lt;/span&gt;
docker &lt;span class="nb"&gt;exec &lt;/span&gt;postgres psql &lt;span class="nt"&gt;-U&lt;/span&gt; admin &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\l&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;        &lt;span class="c"&gt;# List databases&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  MongoDB with Multiple Volumes
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# MongoDB has two important paths:&lt;/span&gt;
&lt;span class="c"&gt;# /data/db       — actual database files&lt;/span&gt;
&lt;span class="c"&gt;# /data/configdb — configuration data&lt;/span&gt;

docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; mongodb &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; mongo_data:/data/db &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; mongo_config:/data/configdb &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;MONGO_INITDB_ROOT_USERNAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;admin &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;MONGO_INITDB_ROOT_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;securepass &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-p&lt;/span&gt; 127.0.0.1:27017:27017 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--restart&lt;/span&gt; unless-stopped &lt;span class="se"&gt;\&lt;/span&gt;
  mongo:7

&lt;span class="c"&gt;# Verify&lt;/span&gt;
docker &lt;span class="nb"&gt;exec &lt;/span&gt;mongodb mongosh &lt;span class="nt"&gt;-u&lt;/span&gt; admin &lt;span class="nt"&gt;-p&lt;/span&gt; securepass &lt;span class="nt"&gt;--eval&lt;/span&gt; &lt;span class="s2"&gt;"db.adminCommand('listDatabases')"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Backup Strategies
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# ─── VOLUME BACKUP (any type of data) ─────────────────────&lt;/span&gt;
&lt;span class="c"&gt;# Use a temporary container — read the volume, create a tar archive&lt;/span&gt;
docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; mydb_data:/data &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;/backups:/backup &lt;span class="se"&gt;\&lt;/span&gt;
  alpine &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nb"&gt;tar &lt;/span&gt;czf /backup/mydb-&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%Y%m%d-%H%M%S&lt;span class="si"&gt;)&lt;/span&gt;.tar.gz /data

&lt;span class="c"&gt;# Creates a file in /backup: mydb-20240115-142030.tar.gz&lt;/span&gt;

&lt;span class="c"&gt;# ─── VOLUME RESTORE ────────────────────────────────────────&lt;/span&gt;
docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; mydb_data:/data &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;/backups:/backup &lt;span class="se"&gt;\&lt;/span&gt;
  alpine &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nb"&gt;tar &lt;/span&gt;xzf /backup/mydb-20240115-142030.tar.gz &lt;span class="nt"&gt;-C&lt;/span&gt; /

&lt;span class="c"&gt;# ─── POSTGRES DUMP (recommended for PostgreSQL) ────────────&lt;/span&gt;
&lt;span class="c"&gt;# Logical backup — portable and version-aware&lt;/span&gt;
docker &lt;span class="nb"&gt;exec &lt;/span&gt;postgres pg_dump &lt;span class="nt"&gt;-U&lt;/span&gt; admin myapp &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; backup-&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%Y%m%d&lt;span class="si"&gt;)&lt;/span&gt;.sql

&lt;span class="c"&gt;# Restore&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; postgres psql &lt;span class="nt"&gt;-U&lt;/span&gt; admin myapp &amp;lt; backup-20240115.sql

&lt;span class="c"&gt;# Compressed backup&lt;/span&gt;
docker &lt;span class="nb"&gt;exec &lt;/span&gt;postgres pg_dump &lt;span class="nt"&gt;-U&lt;/span&gt; admin myapp | &lt;span class="nb"&gt;gzip&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; backup.sql.gz

&lt;span class="c"&gt;# Restore compressed&lt;/span&gt;
&lt;span class="nb"&gt;gunzip&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; backup.sql.gz | docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; postgres psql &lt;span class="nt"&gt;-U&lt;/span&gt; admin myapp

&lt;span class="c"&gt;# ─── MONGODB DUMP ──────────────────────────────────────────&lt;/span&gt;
docker &lt;span class="nb"&gt;exec &lt;/span&gt;mongodb mongodump &lt;span class="nt"&gt;-u&lt;/span&gt; admin &lt;span class="nt"&gt;-p&lt;/span&gt; securepass &lt;span class="nt"&gt;--out&lt;/span&gt; /tmp/backup
docker &lt;span class="nb"&gt;cp &lt;/span&gt;mongodb:/tmp/backup ./mongodb-backup

&lt;span class="c"&gt;# Restore&lt;/span&gt;
docker &lt;span class="nb"&gt;cp&lt;/span&gt; ./mongodb-backup mongodb:/tmp/backup
docker &lt;span class="nb"&gt;exec &lt;/span&gt;mongodb mongorestore &lt;span class="nt"&gt;-u&lt;/span&gt; admin &lt;span class="nt"&gt;-p&lt;/span&gt; securepass /tmp/backup
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Shared Volumes Between Containers
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Pattern: one container writes, another reads&lt;/span&gt;
&lt;span class="c"&gt;# No network communication required between them!&lt;/span&gt;
docker volume create shared-logs

&lt;span class="c"&gt;# App container — writes logs&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; app1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; shared-logs:/var/log/app &lt;span class="se"&gt;\&lt;/span&gt;
  myapp:latest

&lt;span class="c"&gt;# Log viewer — read-only access to the same volume&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; log-viewer &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; shared-logs:/logs:ro &lt;span class="se"&gt;\&lt;/span&gt;
  log-viewer-app:latest

&lt;span class="c"&gt;# log-viewer can now read all of app1's logs&lt;/span&gt;
&lt;span class="c"&gt;# No network communication needed!&lt;/span&gt;

&lt;span class="c"&gt;# Real use case: Nginx serving React static files&lt;/span&gt;
docker volume create static-files

&lt;span class="c"&gt;# Build container: build React, copy to volume&lt;/span&gt;
docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; static-files:/app/build &lt;span class="se"&gt;\&lt;/span&gt;
  react-builder:latest

&lt;span class="c"&gt;# Nginx container: serve files from the same volume&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; nginx &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; static-files:/usr/share/nginx/html:ro &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-p&lt;/span&gt; 80:80 &lt;span class="se"&gt;\&lt;/span&gt;
  nginx:alpine
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Cloud Volume Drivers — AWS EBS, Azure Disk
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# AWS EBS-backed volumes (rexray plugin)&lt;/span&gt;
&lt;span class="c"&gt;# If the host is replaced, data remains safe on AWS EBS!&lt;/span&gt;

&lt;span class="c"&gt;# Install the plugin&lt;/span&gt;
docker plugin &lt;span class="nb"&gt;install &lt;/span&gt;rexray/ebs &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nv"&gt;EBS_ACCESSKEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;AKIAIOSFODNN7EXAMPLE &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nv"&gt;EBS_SECRETKEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY

&lt;span class="c"&gt;# Create an EBS-backed volume&lt;/span&gt;
docker volume create &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--driver&lt;/span&gt; rexray/ebs &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--opt&lt;/span&gt; &lt;span class="nv"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;20 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--opt&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;gp3 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--opt&lt;/span&gt; &lt;span class="nv"&gt;iops&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3000 &lt;span class="se"&gt;\&lt;/span&gt;
  myapp-data

&lt;span class="c"&gt;# Use it — exactly like a regular volume&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; postgres &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; myapp-data:/var/lib/postgresql/data &lt;span class="se"&gt;\&lt;/span&gt;
  postgres:16

&lt;span class="c"&gt;# If the host crashes, detach and reattach the EBS volume to a new host&lt;/span&gt;
&lt;span class="c"&gt;# Data is fully safe on AWS!&lt;/span&gt;

&lt;span class="c"&gt;# For Azure: the cloudstor plugin is available&lt;/span&gt;
&lt;span class="c"&gt;# For GCP: use the gcepd driver&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;strong&gt;Production Reality: Cloud Volumes = True Persistence.&lt;/strong&gt; Local named volumes are tied to the Docker host — if the host machine is lost, the volumes are gone (EBS is not). In real production, use &lt;strong&gt;AWS EFS/EBS&lt;/strong&gt;, &lt;strong&gt;Azure Managed Disks&lt;/strong&gt;, or &lt;strong&gt;GCP Persistent Disk&lt;/strong&gt; via volume drivers. Or better yet, use managed databases (RDS, Atlas) — that is the preferred approach.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; &lt;strong&gt;Volume Best Practices Summary.&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Production databases:&lt;/strong&gt; Named volumes or cloud-backed volumes (prefer RDS)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Development:&lt;/strong&gt; Bind mounts for code, named volumes for databases&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Secrets/session data:&lt;/strong&gt; tmpfs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backups:&lt;/strong&gt; Automated cron + pg_dump/mongodump stored on separate storage&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Never:&lt;/strong&gt; Store critical data only in the container's writable layer&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;

&lt;p&gt;Also make sure your &lt;code&gt;.env&lt;/code&gt; files are correctly structured — you can validate them with the &lt;a href="https://opscanopy.com/env-example-checker/" rel="noopener noreferrer"&gt;.env Example Checker&lt;/a&gt; before deploying. Once your stack is wired up, put these skills to work with real scenarios in &lt;a href="https://opscanopy.com/learn/guides/devops-projects/" rel="noopener noreferrer"&gt;Hands-on DevOps Projects&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Docker Compose Introduction
&lt;/h2&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;br&gt;
&lt;/p&gt;
One Compose file declares the full application stack; &lt;code&gt;docker compose up&lt;/code&gt; creates all services on a shared network with automatic DNS so containers resolve each other by service name.



&lt;h3&gt;
  
  
  Why Docker Compose Exists
&lt;/h3&gt;

&lt;p&gt;Real-world applications do not fit in a single container. A MERN stack application requires:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;MongoDB&lt;/strong&gt; — database&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Express/Node&lt;/strong&gt; — backend API&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;React&lt;/strong&gt; — frontend (served via Nginx)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Redis&lt;/strong&gt; — caching / sessions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without Compose, everything must be managed manually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Without Compose — run all of this by hand!&lt;/span&gt;
docker network create myapp-net

docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; mongodb &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--network&lt;/span&gt; myapp-net &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;MONGO_INITDB_ROOT_USERNAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;admin &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;MONGO_INITDB_ROOT_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;secret &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; mongo_data:/data/db &lt;span class="se"&gt;\&lt;/span&gt;
  mongo:7.0

docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; api &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--network&lt;/span&gt; myapp-net &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;MONGODB_URI&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;mongodb://admin:secret@mongodb:27017/myapp &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-p&lt;/span&gt; 5000:5000 &lt;span class="se"&gt;\&lt;/span&gt;
  myapp-api:latest

docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; frontend &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--network&lt;/span&gt; myapp-net &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-p&lt;/span&gt; 3000:80 &lt;span class="se"&gt;\&lt;/span&gt;
  myapp-frontend:latest

&lt;span class="c"&gt;# Repeat all of this on every restart...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Just as &lt;code&gt;package.json&lt;/code&gt; lists all npm dependencies and &lt;code&gt;npm install&lt;/code&gt; sets everything up — &lt;code&gt;docker-compose.yml&lt;/code&gt; defines all your containers and &lt;code&gt;docker compose up&lt;/code&gt; starts everything in one command.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;With Compose, the same setup becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# With Compose — just this!&lt;/span&gt;
docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt;

&lt;span class="c"&gt;# To shut everything down:&lt;/span&gt;
docker compose down
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Convert an existing command with the &lt;a href="https://opscanopy.com/docker-run-to-compose/" rel="noopener noreferrer"&gt;Docker run → Compose converter&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Compose v1 vs Compose v2 — What Is the Difference?
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Compose v1 (Old)&lt;/th&gt;
&lt;th&gt;Compose v2 (Current)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Command&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;docker-compose&lt;/code&gt; (with dash)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;docker compose&lt;/code&gt; (no dash!)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Installation&lt;/td&gt;
&lt;td&gt;Separate Python binary&lt;/td&gt;
&lt;td&gt;Built-in Docker plugin&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Status&lt;/td&gt;
&lt;td&gt;Deprecated (2023)&lt;/td&gt;
&lt;td&gt;Active, standard&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Performance&lt;/td&gt;
&lt;td&gt;Slower&lt;/td&gt;
&lt;td&gt;Faster (Go-based)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Features&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;td&gt;Full feature set&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026 standard&lt;/td&gt;
&lt;td&gt;Do not use&lt;/td&gt;
&lt;td&gt;Use this!&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; Use &lt;code&gt;docker compose&lt;/code&gt; (no dash) — this is Compose v2 and the current standard. If an older tutorial uses &lt;code&gt;docker-compose&lt;/code&gt; (with a dash), it is using v1 syntax. V2 is mostly backward compatible, but make it a habit to use v2.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check version — should show v2.x.x&lt;/span&gt;
docker compose version
&lt;span class="c"&gt;# Output: Docker Compose version v2.24.5&lt;/span&gt;

&lt;span class="c"&gt;# Old v1 (deprecated — do not use!)&lt;/span&gt;
docker-compose version

&lt;span class="c"&gt;# If the Compose plugin is missing:&lt;/span&gt;
&lt;span class="c"&gt;# Ubuntu/Debian&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;docker-compose-plugin

&lt;span class="c"&gt;# RHEL/CentOS/Fedora&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install &lt;/span&gt;docker-compose-plugin

&lt;span class="c"&gt;# macOS (auto-bundled with Docker Desktop)&lt;/span&gt;
&lt;span class="c"&gt;# Windows (auto-bundled with Docker Desktop)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Compose vs Kubernetes — When to Use Which?
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Use Case&lt;/th&gt;
&lt;th&gt;Compose&lt;/th&gt;
&lt;th&gt;Kubernetes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Local development&lt;/td&gt;
&lt;td&gt;Perfect choice&lt;/td&gt;
&lt;td&gt;Overkill&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Small apps (&amp;lt; 5 containers)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Overkill&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Single server deployment&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Not needed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Auto-scaling&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi-host clusters&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Zero-downtime deploys&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Production at scale&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Learning curve&lt;/td&gt;
&lt;td&gt;Easy (YAML)&lt;/td&gt;
&lt;td&gt;Steep&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Startup / small teams&lt;/td&gt;
&lt;td&gt;Best fit&lt;/td&gt;
&lt;td&gt;Overkill initially&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Start with Compose. Run a MERN stack locally using Compose; Compose is sufficient for a single VPS in production too. When traffic grows and auto-scaling becomes necessary, then invest in learning Kubernetes. The progression is: &lt;strong&gt;Docker → Compose → Kubernetes&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Compose File Structure
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Annotated docker-compose.yml Structure
&lt;/h3&gt;

&lt;p&gt;Understanding the full structure allows you to read any Compose file with ease:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# docker-compose.yml&lt;/span&gt;
&lt;span class="c1"&gt;# In Compose v2, the version field is optional/deprecated&lt;/span&gt;
&lt;span class="c1"&gt;# version: '3.8'  ← no longer required!&lt;/span&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;          &lt;span class="c1"&gt;# All containers are defined here&lt;/span&gt;
  &lt;span class="na"&gt;web&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;             &lt;span class="c1"&gt;# Service name (also becomes the DNS hostname!)&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx&lt;/span&gt;   &lt;span class="c1"&gt;# Which image to use&lt;/span&gt;
    &lt;span class="c1"&gt;# --- OR ---&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;       &lt;span class="c1"&gt;# Build from a local Dockerfile&lt;/span&gt;

  &lt;span class="na"&gt;api&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;             &lt;span class="c1"&gt;# Another service&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./api&lt;/span&gt;               &lt;span class="c1"&gt;# Build context folder&lt;/span&gt;
      &lt;span class="na"&gt;dockerfile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Dockerfile.prod&lt;/span&gt;  &lt;span class="c1"&gt;# Specific Dockerfile name&lt;/span&gt;

  &lt;span class="na"&gt;db&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;              &lt;span class="c1"&gt;# Database service&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres:16&lt;/span&gt;

&lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;          &lt;span class="c1"&gt;# Define custom Docker networks&lt;/span&gt;
  &lt;span class="na"&gt;frontend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;        &lt;span class="c1"&gt;# Frontend network (public-facing)&lt;/span&gt;
  &lt;span class="na"&gt;backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;         &lt;span class="c1"&gt;# Backend network (private, for DB communication)&lt;/span&gt;

&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;           &lt;span class="c1"&gt;# Named volumes (data persistence)&lt;/span&gt;
  &lt;span class="na"&gt;db_data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;         &lt;span class="c1"&gt;# Database files stored here&lt;/span&gt;
  &lt;span class="na"&gt;redis_data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;      &lt;span class="c1"&gt;# Redis data persistence&lt;/span&gt;

&lt;span class="na"&gt;configs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;           &lt;span class="c1"&gt;# Inject config files&lt;/span&gt;
  &lt;span class="na"&gt;nginx_config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./nginx.conf&lt;/span&gt;

&lt;span class="na"&gt;secrets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;           &lt;span class="c1"&gt;# Sensitive data (mainly for Swarm mode)&lt;/span&gt;
  &lt;span class="na"&gt;db_password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./secrets/db_password.txt&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; &lt;strong&gt;Service Name = DNS Hostname!&lt;/strong&gt; In Compose, the service name automatically becomes the DNS hostname. If a service is named &lt;code&gt;db&lt;/code&gt;, other containers can connect to it via &lt;code&gt;db:5432&lt;/code&gt; — not &lt;code&gt;localhost&lt;/code&gt; or an IP. Compose handles this automatically via Docker networking.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  YAML Best Practices in Compose
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Use quotes for strings containing special characters&lt;/span&gt;
&lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;DATABASE_URL="postgresql://user:pass@db:5432/mydb"&lt;/span&gt;

&lt;span class="c1"&gt;# Use literal block (|) for multiline commands&lt;/span&gt;
&lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
  &lt;span class="s"&gt;sh -c "&lt;/span&gt;
    &lt;span class="s"&gt;until pg_isready; do sleep 1; done&lt;/span&gt;
    &lt;span class="s"&gt;python manage.py migrate&lt;/span&gt;
    &lt;span class="s"&gt;python manage.py runserver 0.0.0.0:8000&lt;/span&gt;
  &lt;span class="s"&gt;"&lt;/span&gt;

&lt;span class="c1"&gt;# YAML Anchors — avoid repetition (DRY principle)&lt;/span&gt;
&lt;span class="na"&gt;x-common-env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nl"&gt;&amp;amp;common-env&lt;/span&gt;      &lt;span class="c1"&gt;# &amp;amp;common-env = define the anchor&lt;/span&gt;
  &lt;span class="na"&gt;NODE_ENV&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;production&lt;/span&gt;
  &lt;span class="na"&gt;LOG_LEVEL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;info&lt;/span&gt;
  &lt;span class="na"&gt;TZ&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Asia/Kolkata&lt;/span&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;api&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*common-env&lt;/span&gt;          &lt;span class="c1"&gt;# *common-env = use the anchor (merge)&lt;/span&gt;
      &lt;span class="na"&gt;PORT&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3000&lt;/span&gt;
      &lt;span class="na"&gt;SERVICE_NAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;api&lt;/span&gt;

  &lt;span class="na"&gt;worker&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*common-env&lt;/span&gt;          &lt;span class="c1"&gt;# Reuse the same env vars&lt;/span&gt;
      &lt;span class="na"&gt;WORKER_TYPE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;email&lt;/span&gt;
      &lt;span class="na"&gt;CONCURRENCY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;4"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;strong&gt;YAML Anchors — DRY Principle.&lt;/strong&gt; YAML anchors (&lt;code&gt;&amp;amp;name&lt;/code&gt;) and aliases (&lt;code&gt;*name&lt;/code&gt;) let you write common configuration once and reuse it across multiple services. &lt;code&gt;&amp;lt;&amp;lt;: *common-env&lt;/code&gt; means "merge all key-value pairs here." This is the same as the spread operator (&lt;code&gt;...commonProps&lt;/code&gt;) in JavaScript.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Override Files Pattern — Multiple Environments
&lt;/h3&gt;

&lt;p&gt;One codebase, different configurations per environment — this pattern is used in production-grade applications:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# File structure:&lt;/span&gt;
docker-compose.yml              &lt;span class="c"&gt;# Base config (common settings)&lt;/span&gt;
docker-compose.override.yml     &lt;span class="c"&gt;# Dev overrides — AUTOMATICALLY loaded!&lt;/span&gt;
docker-compose.dev.yml          &lt;span class="c"&gt;# Development specifics&lt;/span&gt;
docker-compose.prod.yml         &lt;span class="c"&gt;# Production specifics&lt;/span&gt;
docker-compose.test.yml         &lt;span class="c"&gt;# Testing / CI specifics&lt;/span&gt;

&lt;span class="c"&gt;# Development (auto-loads base + override):&lt;/span&gt;
docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt;

&lt;span class="c"&gt;# Production (specify files explicitly):&lt;/span&gt;
docker compose &lt;span class="nt"&gt;-f&lt;/span&gt; docker-compose.yml &lt;span class="nt"&gt;-f&lt;/span&gt; docker-compose.prod.yml up &lt;span class="nt"&gt;-d&lt;/span&gt;

&lt;span class="c"&gt;# Testing:&lt;/span&gt;
docker compose &lt;span class="nt"&gt;-f&lt;/span&gt; docker-compose.yml &lt;span class="nt"&gt;-f&lt;/span&gt; docker-compose.test.yml up &lt;span class="nt"&gt;-d&lt;/span&gt;

&lt;span class="c"&gt;# Validate the final merged config:&lt;/span&gt;
docker compose &lt;span class="nt"&gt;-f&lt;/span&gt; docker-compose.yml &lt;span class="nt"&gt;-f&lt;/span&gt; docker-compose.prod.yml config
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# docker-compose.yml (base)&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;api&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myapp-api:latest&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;NODE_ENV&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;production&lt;/span&gt;

&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="c1"&gt;# docker-compose.override.yml (dev — auto-loaded!)&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;api&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;                    &lt;span class="c1"&gt;# Build locally in dev&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./src:/app/src&lt;/span&gt;          &lt;span class="c1"&gt;# Bind mount for hot reload&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;NODE_ENV&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;development&lt;/span&gt;
      &lt;span class="na"&gt;DEBUG&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;true"&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;5000:5000"&lt;/span&gt;             &lt;span class="c1"&gt;# Expose port directly in dev&lt;/span&gt;

&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="c1"&gt;# docker-compose.prod.yml&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;api&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
    &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;limits&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;512M&lt;/span&gt;
    &lt;span class="na"&gt;logging&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;driver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;json-file&lt;/span&gt;
      &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;max-size&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;10m"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; &lt;strong&gt;Best Practice: Override Files.&lt;/strong&gt; Keep common configuration in the base &lt;code&gt;docker-compose.yml&lt;/code&gt;. Put environment-specific settings in override files. &lt;code&gt;docker-compose.override.yml&lt;/code&gt; loads automatically for development — place bind mounts, debug ports, and dev-only tools there. The production file should contain resource limits, proper restart policies, and logging configuration.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Service Configuration Deep Dive
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Complete Service Configuration Reference
&lt;/h3&gt;

&lt;p&gt;All the options available in a service, annotated:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;myapp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# ──────────────────────────────────────────────&lt;/span&gt;
    &lt;span class="c1"&gt;# IMAGE vs BUILD — choose one&lt;/span&gt;
    &lt;span class="c1"&gt;# ──────────────────────────────────────────────&lt;/span&gt;
    &lt;span class="c1"&gt;# Option 1: Use a pre-built image&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx:1.25-alpine&lt;/span&gt;

    &lt;span class="c1"&gt;# Option 2: Build from a local Dockerfile&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;                    &lt;span class="c1"&gt;# Build context (folder with Dockerfile)&lt;/span&gt;
      &lt;span class="na"&gt;dockerfile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Dockerfile&lt;/span&gt;        &lt;span class="c1"&gt;# Which Dockerfile? (default: Dockerfile)&lt;/span&gt;
      &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;                         &lt;span class="c1"&gt;# Build-time arguments (ARG in Dockerfile)&lt;/span&gt;
        &lt;span class="na"&gt;NODE_ENV&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;production&lt;/span&gt;
        &lt;span class="na"&gt;APP_VERSION&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1.0.0"&lt;/span&gt;
      &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;production&lt;/span&gt;            &lt;span class="c1"&gt;# Specific stage in a multi-stage build&lt;/span&gt;

    &lt;span class="c1"&gt;# ──────────────────────────────────────────────&lt;/span&gt;
    &lt;span class="c1"&gt;# CONTAINER NAME&lt;/span&gt;
    &lt;span class="c1"&gt;# ──────────────────────────────────────────────&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-nginx&lt;/span&gt;
    &lt;span class="c1"&gt;# Warning: setting container_name prevents scaling!&lt;/span&gt;
    &lt;span class="c1"&gt;# docker compose up --scale myapp=3 will fail if container_name is set&lt;/span&gt;

    &lt;span class="c1"&gt;# ──────────────────────────────────────────────&lt;/span&gt;
    &lt;span class="c1"&gt;# PORTS — host:container&lt;/span&gt;
    &lt;span class="c1"&gt;# ──────────────────────────────────────────────&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8080:80"&lt;/span&gt;                   &lt;span class="c1"&gt;# host 8080 → container 80&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;443:443"&lt;/span&gt;                   &lt;span class="c1"&gt;# HTTPS&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;127.0.0.1:9090:9090"&lt;/span&gt;       &lt;span class="c1"&gt;# Localhost only (secure!)&lt;/span&gt;

    &lt;span class="c1"&gt;# ──────────────────────────────────────────────&lt;/span&gt;
    &lt;span class="c1"&gt;# ENVIRONMENT VARIABLES&lt;/span&gt;
    &lt;span class="c1"&gt;# ──────────────────────────────────────────────&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;NODE_ENV&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;production&lt;/span&gt;
      &lt;span class="na"&gt;DB_HOST&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;db&lt;/span&gt;                   &lt;span class="c1"&gt;# Use the service name — not localhost!&lt;/span&gt;
      &lt;span class="na"&gt;DB_PORT&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;5432"&lt;/span&gt;               &lt;span class="c1"&gt;# Use quotes for numeric values&lt;/span&gt;
      &lt;span class="na"&gt;DB_NAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mydb&lt;/span&gt;

    &lt;span class="c1"&gt;# Load from env_file&lt;/span&gt;
    &lt;span class="na"&gt;env_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.env&lt;/span&gt;                        &lt;span class="c1"&gt;# Main .env file&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.env.production&lt;/span&gt;             &lt;span class="c1"&gt;# Production-specific variables&lt;/span&gt;

    &lt;span class="c1"&gt;# ──────────────────────────────────────────────&lt;/span&gt;
    &lt;span class="c1"&gt;# VOLUMES — data persistence and bind mounts&lt;/span&gt;
    &lt;span class="c1"&gt;# ──────────────────────────────────────────────&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./src:/app/src:ro&lt;/span&gt;           &lt;span class="c1"&gt;# Bind mount, read-only (dev hot reload)&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;db_data:/var/lib/postgresql/data&lt;/span&gt;  &lt;span class="c1"&gt;# Named volume (production data)&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/etc/localtime:/etc/localtime:ro&lt;/span&gt;  &lt;span class="c1"&gt;# Host timezone inside container&lt;/span&gt;

    &lt;span class="c1"&gt;# ──────────────────────────────────────────────&lt;/span&gt;
    &lt;span class="c1"&gt;# NETWORKS&lt;/span&gt;
    &lt;span class="c1"&gt;# ──────────────────────────────────────────────&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;frontend&lt;/span&gt;                    &lt;span class="c1"&gt;# Public-facing network&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;backend&lt;/span&gt;                     &lt;span class="c1"&gt;# Private network (for DB communication)&lt;/span&gt;

    &lt;span class="c1"&gt;# ──────────────────────────────────────────────&lt;/span&gt;
    &lt;span class="c1"&gt;# DEPENDS_ON — startup order&lt;/span&gt;
    &lt;span class="c1"&gt;# ──────────────────────────────────────────────&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;db&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;service_healthy&lt;/span&gt;  &lt;span class="c1"&gt;# Wait until db is healthy&lt;/span&gt;
      &lt;span class="na"&gt;redis&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;service_started&lt;/span&gt;  &lt;span class="c1"&gt;# Just started, not necessarily healthy&lt;/span&gt;

    &lt;span class="c1"&gt;# ──────────────────────────────────────────────&lt;/span&gt;
    &lt;span class="c1"&gt;# RESTART POLICY&lt;/span&gt;
    &lt;span class="c1"&gt;# ──────────────────────────────────────────────&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
    &lt;span class="c1"&gt;# Options:&lt;/span&gt;
    &lt;span class="c1"&gt;# no           — do not restart (default)&lt;/span&gt;
    &lt;span class="c1"&gt;# always       — always restart&lt;/span&gt;
    &lt;span class="c1"&gt;# on-failure   — restart only on failure&lt;/span&gt;
    &lt;span class="c1"&gt;# unless-stopped — always restart unless manually stopped&lt;/span&gt;

    &lt;span class="c1"&gt;# ──────────────────────────────────────────────&lt;/span&gt;
    &lt;span class="c1"&gt;# COMMAND and ENTRYPOINT&lt;/span&gt;
    &lt;span class="c1"&gt;# ──────────────────────────────────────────────&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm start&lt;/span&gt;              &lt;span class="c1"&gt;# String form&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;node"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;server.js"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;  &lt;span class="c1"&gt;# Array form (preferred — shell injection safe)&lt;/span&gt;

    &lt;span class="na"&gt;entrypoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./docker-entrypoint.sh"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

    &lt;span class="c1"&gt;# ──────────────────────────────────────────────&lt;/span&gt;
    &lt;span class="c1"&gt;# HEALTHCHECK&lt;/span&gt;
    &lt;span class="c1"&gt;# ──────────────────────────────────────────────&lt;/span&gt;
    &lt;span class="na"&gt;healthcheck&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CMD"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;curl"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-f"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:3000/health"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;30s&lt;/span&gt;       &lt;span class="c1"&gt;# Check every 30 seconds&lt;/span&gt;
      &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10s&lt;/span&gt;        &lt;span class="c1"&gt;# Response must arrive within 10 seconds&lt;/span&gt;
      &lt;span class="na"&gt;retries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;          &lt;span class="c1"&gt;# Mark unhealthy after 3 failures&lt;/span&gt;
      &lt;span class="na"&gt;start_period&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;40s&lt;/span&gt;   &lt;span class="c1"&gt;# Wait this long after startup before checking&lt;/span&gt;

    &lt;span class="c1"&gt;# ──────────────────────────────────────────────&lt;/span&gt;
    &lt;span class="c1"&gt;# DEPLOY — resource limits&lt;/span&gt;
    &lt;span class="c1"&gt;# ──────────────────────────────────────────────&lt;/span&gt;
    &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;limits&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;cpus&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0.50"&lt;/span&gt;            &lt;span class="c1"&gt;# Max 50% of one CPU core&lt;/span&gt;
          &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;512M&lt;/span&gt;            &lt;span class="c1"&gt;# Max 512 MB RAM&lt;/span&gt;
        &lt;span class="na"&gt;reservations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;cpus&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0.25"&lt;/span&gt;            &lt;span class="c1"&gt;# Minimum guaranteed CPU&lt;/span&gt;
          &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;256M&lt;/span&gt;            &lt;span class="c1"&gt;# Minimum guaranteed RAM&lt;/span&gt;

    &lt;span class="c1"&gt;# ──────────────────────────────────────────────&lt;/span&gt;
    &lt;span class="c1"&gt;# PROFILES — optional services&lt;/span&gt;
    &lt;span class="c1"&gt;# ──────────────────────────────────────────────&lt;/span&gt;
    &lt;span class="na"&gt;profiles&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;debug&lt;/span&gt;   &lt;span class="c1"&gt;# Only starts with: docker compose --profile debug up&lt;/span&gt;

    &lt;span class="c1"&gt;# ──────────────────────────────────────────────&lt;/span&gt;
    &lt;span class="c1"&gt;# LOGGING&lt;/span&gt;
    &lt;span class="c1"&gt;# ──────────────────────────────────────────────&lt;/span&gt;
    &lt;span class="na"&gt;logging&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;driver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;json-file&lt;/span&gt;
      &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;max-size&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;10m"&lt;/span&gt;     &lt;span class="c1"&gt;# Log file max 10 MB&lt;/span&gt;
        &lt;span class="na"&gt;max-file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3"&lt;/span&gt;       &lt;span class="c1"&gt;# Max 3 log files (then rotate)&lt;/span&gt;

    &lt;span class="c1"&gt;# ──────────────────────────────────────────────&lt;/span&gt;
    &lt;span class="c1"&gt;# EXTRA HOSTS — /etc/hosts entries&lt;/span&gt;
    &lt;span class="c1"&gt;# ──────────────────────────────────────────────&lt;/span&gt;
    &lt;span class="na"&gt;extra_hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;host.docker.internal:host-gateway"&lt;/span&gt;  &lt;span class="c1"&gt;# Connect to host machine&lt;/span&gt;

    &lt;span class="c1"&gt;# ──────────────────────────────────────────────&lt;/span&gt;
    &lt;span class="c1"&gt;# DNS — custom DNS servers&lt;/span&gt;
    &lt;span class="c1"&gt;# ──────────────────────────────────────────────&lt;/span&gt;
    &lt;span class="na"&gt;dns&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;8.8.8.8&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;8.8.4.4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  depends_on and Healthchecks — A Common Gotcha
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; &lt;strong&gt;The Default depends_on Behavior Is Misleading!&lt;/strong&gt; A simple &lt;code&gt;depends_on: [db]&lt;/code&gt; only guarantees that the &lt;code&gt;db&lt;/code&gt; container has &lt;em&gt;started&lt;/em&gt; — not that it is &lt;em&gt;ready&lt;/em&gt;. PostgreSQL takes 5–10 seconds to initialize. If the &lt;code&gt;api&lt;/code&gt; service tries to connect immediately, the connection will fail. Solution: use &lt;code&gt;condition: service_healthy&lt;/code&gt; and define a &lt;code&gt;healthcheck&lt;/code&gt; on the db service.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;db&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres:16-alpine&lt;/span&gt;
    &lt;span class="na"&gt;healthcheck&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CMD-SHELL"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pg_isready&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-U&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;myuser&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-d&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;mydb"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10s&lt;/span&gt;
      &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;5s&lt;/span&gt;
      &lt;span class="na"&gt;retries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
      &lt;span class="na"&gt;start_period&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10s&lt;/span&gt;    &lt;span class="c1"&gt;# Give PostgreSQL time to initialize&lt;/span&gt;

  &lt;span class="na"&gt;api&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;db&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;service_healthy&lt;/span&gt;   &lt;span class="c1"&gt;# Wait until HEALTHY, not just STARTED!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; &lt;strong&gt;Profiles — Make Dev Tools Optional.&lt;/strong&gt; Using &lt;code&gt;profiles&lt;/code&gt;, you can make certain services optional. For example, &lt;code&gt;pgadmin&lt;/code&gt; (a DB GUI) is only needed in development — give it &lt;code&gt;profiles: [debug]&lt;/code&gt;. It will not start during a normal &lt;code&gt;docker compose up&lt;/code&gt;. It will only start with &lt;code&gt;docker compose --profile debug up&lt;/code&gt;. This prevents debug tools from accidentally running in production.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Docker Compose Commands — Complete Reference
&lt;/h2&gt;

&lt;h3&gt;
  
  
  docker compose up — Start Everything
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Basic — start in foreground (Ctrl+C to stop)&lt;/span&gt;
docker compose up

&lt;span class="c"&gt;# Detached — start in background (for production)&lt;/span&gt;
docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt;

&lt;span class="c"&gt;# Rebuild images and start (after code changes)&lt;/span&gt;
docker compose up &lt;span class="nt"&gt;--build&lt;/span&gt;

&lt;span class="c"&gt;# Force recreate containers (even if nothing changed)&lt;/span&gt;
docker compose up &lt;span class="nt"&gt;--force-recreate&lt;/span&gt;

&lt;span class="c"&gt;# Start only specific services&lt;/span&gt;
docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt; api db

&lt;span class="c"&gt;# Run multiple instances (for load balancing)&lt;/span&gt;
docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--scale&lt;/span&gt; &lt;span class="nv"&gt;api&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3
&lt;span class="c"&gt;# Note: container_name must not be set when scaling!&lt;/span&gt;

&lt;span class="c"&gt;# Start with a specific profile&lt;/span&gt;
docker compose &lt;span class="nt"&gt;--profile&lt;/span&gt; debug up &lt;span class="nt"&gt;-d&lt;/span&gt;

&lt;span class="c"&gt;# Use a custom .env file&lt;/span&gt;
docker compose &lt;span class="nt"&gt;--env-file&lt;/span&gt; .env.prod up &lt;span class="nt"&gt;-d&lt;/span&gt;

&lt;span class="c"&gt;# Remove orphan containers (not defined in the compose file)&lt;/span&gt;
docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--remove-orphans&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  docker compose down — Stop and Clean Up
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Standard — remove containers and networks&lt;/span&gt;
docker compose down

&lt;span class="c"&gt;# Also remove volumes (DATA LOSS — be careful!)&lt;/span&gt;
docker compose down &lt;span class="nt"&gt;-v&lt;/span&gt;

&lt;span class="c"&gt;# Also remove images&lt;/span&gt;
docker compose down &lt;span class="nt"&gt;--rmi&lt;/span&gt; all     &lt;span class="c"&gt;# All images&lt;/span&gt;
docker compose down &lt;span class="nt"&gt;--rmi&lt;/span&gt; &lt;span class="nb"&gt;local&lt;/span&gt;   &lt;span class="c"&gt;# Only locally built images&lt;/span&gt;

&lt;span class="c"&gt;# Stop but keep containers (do not remove)&lt;/span&gt;
docker compose stop

&lt;span class="c"&gt;# Complete teardown — clean up everything&lt;/span&gt;
docker compose down &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nt"&gt;--rmi&lt;/span&gt; all &lt;span class="nt"&gt;--remove-orphans&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; &lt;strong&gt;docker compose down -v — Data Will Be Deleted!&lt;/strong&gt; The &lt;code&gt;-v&lt;/code&gt; flag removes all named volumes. This means all database data is gone. This is useful for a clean restart in development, but never use it carelessly in production. Take a backup first.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  docker compose logs — Debugging
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Logs from all services&lt;/span&gt;
docker compose logs

&lt;span class="c"&gt;# Follow (live stream)&lt;/span&gt;
docker compose logs &lt;span class="nt"&gt;-f&lt;/span&gt;

&lt;span class="c"&gt;# Logs from a specific service&lt;/span&gt;
docker compose logs &lt;span class="nt"&gt;-f&lt;/span&gt; api

&lt;span class="c"&gt;# Last 100 lines&lt;/span&gt;
docker compose logs &lt;span class="nt"&gt;--tail&lt;/span&gt; 100

&lt;span class="c"&gt;# With timestamps&lt;/span&gt;
docker compose logs &lt;span class="nt"&gt;-t&lt;/span&gt;

&lt;span class="c"&gt;# Multiple services at once&lt;/span&gt;
docker compose logs &lt;span class="nt"&gt;-f&lt;/span&gt; api db

&lt;span class="c"&gt;# Last 1 hour&lt;/span&gt;
docker compose logs &lt;span class="nt"&gt;--since&lt;/span&gt; 1h

&lt;span class="c"&gt;# From a specific time&lt;/span&gt;
docker compose logs &lt;span class="nt"&gt;--since&lt;/span&gt; &lt;span class="s2"&gt;"2024-01-15T10:00:00"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  docker compose exec vs run — What Is the Difference?
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;exec&lt;/th&gt;
&lt;th&gt;run&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Container&lt;/td&gt;
&lt;td&gt;Runs inside an existing running container&lt;/td&gt;
&lt;td&gt;Starts a new container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Use case&lt;/td&gt;
&lt;td&gt;Debug a running service&lt;/td&gt;
&lt;td&gt;One-off commands (migrations, etc.)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Service running?&lt;/td&gt;
&lt;td&gt;Must be running&lt;/td&gt;
&lt;td&gt;Service does not need to be running&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cleanup&lt;/td&gt;
&lt;td&gt;Container remains as-is&lt;/td&gt;
&lt;td&gt;Auto-cleanup with &lt;code&gt;--rm&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# ── docker compose exec ──────────────────────────────&lt;/span&gt;
&lt;span class="c"&gt;# Run a command in a running container&lt;/span&gt;
docker compose &lt;span class="nb"&gt;exec &lt;/span&gt;api bash
docker compose &lt;span class="nb"&gt;exec &lt;/span&gt;db psql &lt;span class="nt"&gt;-U&lt;/span&gt; admin &lt;span class="nt"&gt;-d&lt;/span&gt; mydb
docker compose &lt;span class="nb"&gt;exec &lt;/span&gt;redis redis-cli

&lt;span class="c"&gt;# Without TTY (for automation/scripts)&lt;/span&gt;
docker compose &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-T&lt;/span&gt; api npm &lt;span class="nb"&gt;test&lt;/span&gt;

&lt;span class="c"&gt;# As a specific user&lt;/span&gt;
docker compose &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;--user&lt;/span&gt; root api bash

&lt;span class="c"&gt;# ── docker compose run ──────────────────────────────&lt;/span&gt;
&lt;span class="c"&gt;# One-off command in a new container&lt;/span&gt;
docker compose run api npm run migrate
docker compose run api python manage.py createsuperuser

&lt;span class="c"&gt;# Auto-cleanup after run&lt;/span&gt;
docker compose run &lt;span class="nt"&gt;--rm&lt;/span&gt; api npm &lt;span class="nb"&gt;test&lt;/span&gt;

&lt;span class="c"&gt;# Override the entrypoint&lt;/span&gt;
docker compose run &lt;span class="nt"&gt;--entrypoint&lt;/span&gt; bash api

&lt;span class="c"&gt;# With custom env vars&lt;/span&gt;
docker compose run &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;DEBUG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true &lt;/span&gt;api npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; &lt;strong&gt;exec vs run — Simple Rule.&lt;/strong&gt; Need to do something in a running service (debug, inspect)? → &lt;code&gt;exec&lt;/code&gt;. Need to run a one-off task alongside the service (DB migration, seed data, test run)? → &lt;code&gt;run --rm&lt;/code&gt;. &lt;code&gt;docker compose run --rm web python manage.py migrate&lt;/code&gt; is the standard pattern for Django/Rails migrations.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Other Useful Commands
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# List services&lt;/span&gt;
docker compose ps
docker compose ps &lt;span class="nt"&gt;-a&lt;/span&gt;             &lt;span class="c"&gt;# Include stopped services&lt;/span&gt;
docker compose ps &lt;span class="nt"&gt;--format&lt;/span&gt; json  &lt;span class="c"&gt;# JSON output&lt;/span&gt;

&lt;span class="c"&gt;# Build only (do not start)&lt;/span&gt;
docker compose build
docker compose build &lt;span class="nt"&gt;--no-cache&lt;/span&gt; api      &lt;span class="c"&gt;# Ignore cache&lt;/span&gt;
docker compose build &lt;span class="nt"&gt;--parallel&lt;/span&gt;          &lt;span class="c"&gt;# Build all in parallel&lt;/span&gt;

&lt;span class="c"&gt;# Pull latest images&lt;/span&gt;
docker compose pull

&lt;span class="c"&gt;# Push images to registry&lt;/span&gt;
docker compose push

&lt;span class="c"&gt;# Validate the compose file (shows final merged config)&lt;/span&gt;
docker compose config

&lt;span class="c"&gt;# Show running processes&lt;/span&gt;
docker compose top

&lt;span class="c"&gt;# Restart services&lt;/span&gt;
docker compose restart
docker compose restart api

&lt;span class="c"&gt;# Pause/Unpause (stop CPU usage, keep memory)&lt;/span&gt;
docker compose pause
docker compose unpause

&lt;span class="c"&gt;# Create without starting&lt;/span&gt;
docker compose create

&lt;span class="c"&gt;# Stream events&lt;/span&gt;
docker compose events
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; &lt;strong&gt;Validate Your Compose File.&lt;/strong&gt; &lt;code&gt;docker compose config&lt;/code&gt; is a very useful command that merges all override files and shows the final YAML. Catch typos, missing variables, and syntax errors before deploying. Use it in CI/CD too: &lt;code&gt;docker compose -f docker-compose.yml -f docker-compose.prod.yml config --quiet&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Production Compose Examples — Real-World Stacks
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Example 1: MERN Stack (React + Node + MongoDB)
&lt;/h3&gt;

&lt;p&gt;Most relevant for React developers — a complete MERN stack with Compose:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# docker-compose.yml — MERN Stack&lt;/span&gt;
&lt;span class="c1"&gt;# Usage: docker compose up -d&lt;/span&gt;
&lt;span class="c1"&gt;# Variables are loaded from a .env file&lt;/span&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# ────────────────────────────────&lt;/span&gt;
  &lt;span class="c1"&gt;# MongoDB Database&lt;/span&gt;
  &lt;span class="c1"&gt;# ────────────────────────────────&lt;/span&gt;
  &lt;span class="na"&gt;mongodb&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mongo:7.0-jammy&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mern-mongodb&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;MONGO_INITDB_ROOT_USERNAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${MONGO_USER:-admin}&lt;/span&gt;
      &lt;span class="na"&gt;MONGO_INITDB_ROOT_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${MONGO_PASSWORD:-password}&lt;/span&gt;
      &lt;span class="na"&gt;MONGO_INITDB_DATABASE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${MONGO_DB:-myapp}&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;mongo_data:/data/db&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;mongo_config:/data/configdb&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./mongo-init.js:/docker-entrypoint-initdb.d/init.js:ro&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;backend&lt;/span&gt;          &lt;span class="c1"&gt;# Backend network only — no public access!&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
    &lt;span class="na"&gt;healthcheck&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CMD"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mongosh"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--eval"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;db.adminCommand('ping')"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;30s&lt;/span&gt;
      &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10s&lt;/span&gt;
      &lt;span class="na"&gt;retries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
      &lt;span class="na"&gt;start_period&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;30s&lt;/span&gt;
    &lt;span class="na"&gt;logging&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;driver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;json-file&lt;/span&gt;
      &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;max-size&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;10m"&lt;/span&gt;
        &lt;span class="na"&gt;max-file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3"&lt;/span&gt;

  &lt;span class="c1"&gt;# ────────────────────────────────&lt;/span&gt;
  &lt;span class="c1"&gt;# Node.js API Server&lt;/span&gt;
  &lt;span class="c1"&gt;# ────────────────────────────────&lt;/span&gt;
  &lt;span class="na"&gt;api&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./api&lt;/span&gt;
      &lt;span class="na"&gt;dockerfile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Dockerfile&lt;/span&gt;
      &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;production&lt;/span&gt;    &lt;span class="c1"&gt;# Use the production stage of a multi-stage build&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mern-api&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;NODE_ENV&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;production&lt;/span&gt;
      &lt;span class="na"&gt;MONGODB_URI&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mongodb://${MONGO_USER:-admin}:${MONGO_PASSWORD:-password}@mongodb:27017/${MONGO_DB:-myapp}?authSource=admin&lt;/span&gt;
      &lt;span class="na"&gt;JWT_SECRET&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${JWT_SECRET:-changeme-in-production}&lt;/span&gt;
      &lt;span class="na"&gt;PORT&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5000&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;api_logs:/app/logs&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;backend&lt;/span&gt;              &lt;span class="c1"&gt;# For MongoDB communication&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;frontend&lt;/span&gt;             &lt;span class="c1"&gt;# For receiving requests from React&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;mongodb&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;service_healthy&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
    &lt;span class="na"&gt;healthcheck&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CMD"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;node"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-e"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;require('http').get('http://localhost:5000/health',&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;r&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;process.exit(r.statusCode&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;===&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;200&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;?&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;0&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;:&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;1))"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;30s&lt;/span&gt;
      &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;5s&lt;/span&gt;
      &lt;span class="na"&gt;retries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
      &lt;span class="na"&gt;start_period&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;20s&lt;/span&gt;
    &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;limits&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;cpus&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0.50"&lt;/span&gt;
          &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;512M&lt;/span&gt;
    &lt;span class="na"&gt;logging&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;driver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;json-file&lt;/span&gt;
      &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;max-size&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;10m"&lt;/span&gt;
        &lt;span class="na"&gt;max-file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3"&lt;/span&gt;

  &lt;span class="c1"&gt;# ────────────────────────────────&lt;/span&gt;
  &lt;span class="c1"&gt;# React Frontend (served via Nginx)&lt;/span&gt;
  &lt;span class="c1"&gt;# ────────────────────────────────&lt;/span&gt;
  &lt;span class="na"&gt;frontend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./frontend&lt;/span&gt;
      &lt;span class="na"&gt;dockerfile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Dockerfile&lt;/span&gt;
      &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;REACT_APP_API_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${API_URL:-http://localhost:5000}&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mern-frontend&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${FRONTEND_PORT:-3000}:80"&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;frontend&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;api&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;service_healthy&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
    &lt;span class="na"&gt;healthcheck&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CMD"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;wget"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-qO-"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost/health"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;30s&lt;/span&gt;
      &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;5s&lt;/span&gt;
      &lt;span class="na"&gt;retries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;

&lt;span class="c1"&gt;# ────────────────────────────────&lt;/span&gt;
&lt;span class="c1"&gt;# Volumes&lt;/span&gt;
&lt;span class="c1"&gt;# ────────────────────────────────&lt;/span&gt;
&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;mongo_data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;driver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;local&lt;/span&gt;
    &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;com.myapp.description=MongoDB&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;volume"&lt;/span&gt;
  &lt;span class="na"&gt;mongo_config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;api_logs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

&lt;span class="c1"&gt;# ────────────────────────────────&lt;/span&gt;
&lt;span class="c1"&gt;# Networks&lt;/span&gt;
&lt;span class="c1"&gt;# ────────────────────────────────&lt;/span&gt;
&lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;frontend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;driver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bridge&lt;/span&gt;             &lt;span class="c1"&gt;# Public traffic comes here&lt;/span&gt;
  &lt;span class="na"&gt;backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;driver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bridge&lt;/span&gt;
    &lt;span class="na"&gt;internal&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;             &lt;span class="c1"&gt;# No external access! Container-to-container only&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;strong&gt;Production Pattern: Internal Networks.&lt;/strong&gt; Apply &lt;code&gt;internal: true&lt;/code&gt; to the backend network. This means MongoDB is never directly accessible from the internet — only the &lt;code&gt;api&lt;/code&gt; service can reach it. This is a security best practice. The flow is: Frontend → API (via frontend network), API → MongoDB (via backend network). MongoDB has no public port exposed.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Example 2: Python Flask + PostgreSQL + Redis + Celery
&lt;/h3&gt;

&lt;p&gt;Full-stack Python application with background task processing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# docker-compose.yml — Flask + Postgres + Redis + Celery&lt;/span&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# ────────────────────────────────&lt;/span&gt;
  &lt;span class="c1"&gt;# PostgreSQL Database&lt;/span&gt;
  &lt;span class="c1"&gt;# ────────────────────────────────&lt;/span&gt;
  &lt;span class="na"&gt;postgres&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres:16-alpine&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_USER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${DB_USER:-myuser}&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${DB_PASSWORD:-mypassword}&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_DB&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${DB_NAME:-mydb}&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;postgres_data:/var/lib/postgresql/data&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./sql/init.sql:/docker-entrypoint-initdb.d/init.sql:ro&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;backend&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
    &lt;span class="na"&gt;healthcheck&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CMD-SHELL"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pg_isready&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-U&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;${DB_USER:-myuser}&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-d&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;${DB_NAME:-mydb}"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10s&lt;/span&gt;
      &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;5s&lt;/span&gt;
      &lt;span class="na"&gt;retries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;

  &lt;span class="c1"&gt;# ────────────────────────────────&lt;/span&gt;
  &lt;span class="c1"&gt;# Redis — Cache + Message Broker&lt;/span&gt;
  &lt;span class="c1"&gt;# ────────────────────────────────&lt;/span&gt;
  &lt;span class="na"&gt;redis&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis:7-alpine&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis-server --appendonly yes --requirepass ${REDIS_PASSWORD:-redis_secret}&lt;/span&gt;
    &lt;span class="c1"&gt;# appendonly yes = persist data even after restart&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;redis_data:/data&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;backend&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
    &lt;span class="na"&gt;healthcheck&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CMD"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;redis-cli"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--pass"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${REDIS_PASSWORD:-redis_secret}"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ping"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10s&lt;/span&gt;
      &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;5s&lt;/span&gt;
      &lt;span class="na"&gt;retries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;

  &lt;span class="c1"&gt;# ────────────────────────────────&lt;/span&gt;
  &lt;span class="c1"&gt;# Flask Web Application&lt;/span&gt;
  &lt;span class="c1"&gt;# ────────────────────────────────&lt;/span&gt;
  &lt;span class="na"&gt;web&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgresql://${DB_USER:-myuser}:${DB_PASSWORD:-mypassword}@postgres:5432/${DB_NAME:-mydb}&lt;/span&gt;
      &lt;span class="na"&gt;REDIS_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis://:${REDIS_PASSWORD:-redis_secret}@redis:6379/0&lt;/span&gt;
      &lt;span class="na"&gt;FLASK_ENV&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${FLASK_ENV:-production}&lt;/span&gt;
      &lt;span class="na"&gt;SECRET_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${SECRET_KEY:-change-in-production}&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${APP_PORT:-5000}:5000"&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;app_logs:/app/logs&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./uploads:/app/uploads&lt;/span&gt;   &lt;span class="c1"&gt;# Persist user uploads&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;postgres&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;service_healthy&lt;/span&gt;
      &lt;span class="na"&gt;redis&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;service_healthy&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;backend&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;frontend&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
    &lt;span class="na"&gt;healthcheck&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CMD"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;curl"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-f"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:5000/health"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;30s&lt;/span&gt;
      &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10s&lt;/span&gt;
      &lt;span class="na"&gt;retries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
      &lt;span class="na"&gt;start_period&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;20s&lt;/span&gt;

  &lt;span class="c1"&gt;# ────────────────────────────────&lt;/span&gt;
  &lt;span class="c1"&gt;# Celery Worker — Background Tasks&lt;/span&gt;
  &lt;span class="c1"&gt;# ────────────────────────────────&lt;/span&gt;
  &lt;span class="na"&gt;celery_worker&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;    &lt;span class="c1"&gt;# Same image as web!&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;celery -A app.celery worker --loglevel=info --concurrency=4&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgresql://${DB_USER:-myuser}:${DB_PASSWORD:-mypassword}@postgres:5432/${DB_NAME:-mydb}&lt;/span&gt;
      &lt;span class="na"&gt;REDIS_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis://:${REDIS_PASSWORD:-redis_secret}@redis:6379/0&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;web&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;redis&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;backend&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
    &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;limits&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;cpus&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1.0"&lt;/span&gt;
          &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;512M&lt;/span&gt;

  &lt;span class="c1"&gt;# ────────────────────────────────&lt;/span&gt;
  &lt;span class="c1"&gt;# Celery Beat — Scheduled Tasks (Cron-like)&lt;/span&gt;
  &lt;span class="c1"&gt;# ────────────────────────────────&lt;/span&gt;
  &lt;span class="na"&gt;celery_beat&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;celery -A app.celery beat --loglevel=info&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgresql://${DB_USER:-myuser}:${DB_PASSWORD:-mypassword}@postgres:5432/${DB_NAME:-mydb}&lt;/span&gt;
      &lt;span class="na"&gt;REDIS_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis://:${REDIS_PASSWORD:-redis_secret}@redis:6379/0&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;web&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;backend&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
    &lt;span class="c1"&gt;# Beat should have only ONE instance — do not scale it!&lt;/span&gt;

  &lt;span class="c1"&gt;# ────────────────────────────────&lt;/span&gt;
  &lt;span class="c1"&gt;# Nginx Reverse Proxy&lt;/span&gt;
  &lt;span class="c1"&gt;# ────────────────────────────────&lt;/span&gt;
  &lt;span class="na"&gt;nginx&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx:1.25-alpine&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;80:80"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;443:443"&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./nginx/nginx.conf:/etc/nginx/conf.d/default.conf:ro&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./nginx/ssl:/etc/nginx/ssl:ro&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;web&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;frontend&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;

&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;postgres_data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;redis_data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;app_logs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

&lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;frontend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;driver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bridge&lt;/span&gt;
  &lt;span class="na"&gt;backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;driver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bridge&lt;/span&gt;
    &lt;span class="na"&gt;internal&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;    &lt;span class="c1"&gt;# DB and Redis must not be directly accessible!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; &lt;strong&gt;Same Image, Different Command!&lt;/strong&gt; Flask web, Celery worker, and Celery beat all use the same &lt;code&gt;build: .&lt;/code&gt; (same Docker image) but with different &lt;code&gt;command&lt;/code&gt; values. This is efficient: build one image, run different processes. When code changes, only one image needs to be rebuilt.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Example 3: WordPress + MySQL
&lt;/h3&gt;

&lt;p&gt;Classic CMS setup — useful for client projects:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# docker-compose.yml — WordPress + MySQL + Nginx&lt;/span&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# ────────────────────────────────&lt;/span&gt;
  &lt;span class="c1"&gt;# MySQL Database&lt;/span&gt;
  &lt;span class="c1"&gt;# ────────────────────────────────&lt;/span&gt;
  &lt;span class="na"&gt;mysql&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mysql:8.0&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;MYSQL_ROOT_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${MYSQL_ROOT_PASSWORD:-rootpassword}&lt;/span&gt;
      &lt;span class="na"&gt;MYSQL_DATABASE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;wordpress&lt;/span&gt;
      &lt;span class="na"&gt;MYSQL_USER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;wordpress&lt;/span&gt;
      &lt;span class="na"&gt;MYSQL_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${MYSQL_PASSWORD:-wppassword}&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;mysql_data:/var/lib/mysql&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;wp-network&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
    &lt;span class="na"&gt;healthcheck&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CMD"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mysqladmin"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ping"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-h"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;localhost"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-u"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;wordpress"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-pwppassword"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;30s&lt;/span&gt;
      &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10s&lt;/span&gt;
      &lt;span class="na"&gt;retries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;

  &lt;span class="c1"&gt;# ────────────────────────────────&lt;/span&gt;
  &lt;span class="c1"&gt;# WordPress (PHP-FPM)&lt;/span&gt;
  &lt;span class="c1"&gt;# ────────────────────────────────&lt;/span&gt;
  &lt;span class="na"&gt;wordpress&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;wordpress:6.4-php8.2-fpm-alpine&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;WORDPRESS_DB_HOST&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mysql:3306&lt;/span&gt;      &lt;span class="c1"&gt;# Use the service name!&lt;/span&gt;
      &lt;span class="na"&gt;WORDPRESS_DB_USER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;wordpress&lt;/span&gt;
      &lt;span class="na"&gt;WORDPRESS_DB_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${MYSQL_PASSWORD:-wppassword}&lt;/span&gt;
      &lt;span class="na"&gt;WORDPRESS_DB_NAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;wordpress&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;wp_content:/var/www/html/wp-content&lt;/span&gt;  &lt;span class="c1"&gt;# Themes, plugins, uploads&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;mysql&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;service_healthy&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;wp-network&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;

  &lt;span class="c1"&gt;# ────────────────────────────────&lt;/span&gt;
  &lt;span class="c1"&gt;# Nginx — Serve static files + PHP-FPM proxy&lt;/span&gt;
  &lt;span class="c1"&gt;# ────────────────────────────────&lt;/span&gt;
  &lt;span class="na"&gt;nginx&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx:1.25-alpine&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;80:80"&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./nginx/wordpress.conf:/etc/nginx/conf.d/default.conf:ro&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;wp_content:/var/www/html/wp-content:ro&lt;/span&gt;   &lt;span class="c1"&gt;# Serve static files directly&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;wordpress&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;wp-network&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;

&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;mysql_data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;    &lt;span class="c1"&gt;# Database files&lt;/span&gt;
  &lt;span class="na"&gt;wp_content&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;    &lt;span class="c1"&gt;# WordPress uploads, themes, plugins&lt;/span&gt;

&lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;wp-network&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;driver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bridge&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Example 4: Monitoring Stack (Prometheus + Grafana + cAdvisor)
&lt;/h3&gt;

&lt;p&gt;Monitoring production applications is essential. This stack tracks CPU, memory, and network usage of Docker containers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# docker-compose.yml — Monitoring Stack&lt;/span&gt;
&lt;span class="c1"&gt;# Usage: docker compose up -d&lt;/span&gt;
&lt;span class="c1"&gt;# Access: Grafana → http://localhost:3000 (admin/admin)&lt;/span&gt;
&lt;span class="c1"&gt;#         Prometheus → http://localhost:9090&lt;/span&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# ────────────────────────────────&lt;/span&gt;
  &lt;span class="c1"&gt;# Prometheus — Metrics Collector&lt;/span&gt;
  &lt;span class="c1"&gt;# ────────────────────────────────&lt;/span&gt;
  &lt;span class="na"&gt;prometheus&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;prom/prometheus:latest&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;9090:9090"&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;prometheus_data:/prometheus&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--config.file=/etc/prometheus/prometheus.yml&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--storage.tsdb.path=/prometheus&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--web.enable-lifecycle&lt;/span&gt;         &lt;span class="c1"&gt;# Enable config hot-reload&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;monitoring&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;

  &lt;span class="c1"&gt;# ────────────────────────────────&lt;/span&gt;
  &lt;span class="c1"&gt;# Grafana — Visualization Dashboard&lt;/span&gt;
  &lt;span class="c1"&gt;# ────────────────────────────────&lt;/span&gt;
  &lt;span class="na"&gt;grafana&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;grafana/grafana:latest&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3000:3000"&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;GF_SECURITY_ADMIN_USER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${GRAFANA_USER:-admin}&lt;/span&gt;
      &lt;span class="na"&gt;GF_SECURITY_ADMIN_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${GRAFANA_PASSWORD:-admin}&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;grafana_data:/var/lib/grafana&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./grafana/dashboards:/etc/grafana/provisioning/dashboards:ro&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./grafana/datasources:/etc/grafana/provisioning/datasources:ro&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;prometheus&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;monitoring&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;

  &lt;span class="c1"&gt;# ────────────────────────────────&lt;/span&gt;
  &lt;span class="c1"&gt;# cAdvisor — Container Metrics&lt;/span&gt;
  &lt;span class="c1"&gt;# ────────────────────────────────&lt;/span&gt;
  &lt;span class="na"&gt;cadvisor&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gcr.io/cadvisor/cadvisor:latest&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/:/rootfs:ro&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/var/run:/var/run:ro&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/sys:/sys:ro&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/var/lib/docker/:/var/lib/docker:ro&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8080:8080"&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;monitoring&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
    &lt;span class="c1"&gt;# cAdvisor communicates directly with the Docker daemon&lt;/span&gt;
    &lt;span class="c1"&gt;# which is why host paths need to be mounted&lt;/span&gt;

  &lt;span class="c1"&gt;# ────────────────────────────────&lt;/span&gt;
  &lt;span class="c1"&gt;# Node Exporter — Host Machine Metrics&lt;/span&gt;
  &lt;span class="c1"&gt;# ────────────────────────────────&lt;/span&gt;
  &lt;span class="na"&gt;node_exporter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;prom/node-exporter:latest&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/proc:/host/proc:ro&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/sys:/host/sys:ro&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/:/rootfs:ro&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--path.procfs=/host/proc&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--path.sysfs=/host/sys&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;9100:9100"&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;monitoring&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
    &lt;span class="c1"&gt;# Node Exporter exposes host CPU, RAM, disk, and network metrics&lt;/span&gt;

&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;prometheus_data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;   &lt;span class="c1"&gt;# Time-series metrics data&lt;/span&gt;
  &lt;span class="na"&gt;grafana_data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;      &lt;span class="c1"&gt;# Dashboards and settings&lt;/span&gt;

&lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;monitoring&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;driver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bridge&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;strong&gt;What the Monitoring Stack Tracks.&lt;/strong&gt; &lt;strong&gt;cAdvisor&lt;/strong&gt; — CPU/RAM/network/disk usage for every Docker container. &lt;strong&gt;Node Exporter&lt;/strong&gt; — hardware metrics for the host machine. &lt;strong&gt;Prometheus&lt;/strong&gt; — collects and stores all data. &lt;strong&gt;Grafana&lt;/strong&gt; — visualizes everything in dashboards. This monitoring setup is essential in production: you cannot improve what you cannot measure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;strong&gt;Production Checklist for Compose Apps.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add &lt;code&gt;restart: unless-stopped&lt;/code&gt; to all services&lt;/li&gt;
&lt;li&gt;Configure &lt;code&gt;healthcheck&lt;/code&gt; on database services&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;internal: true&lt;/code&gt; on networks for sensitive services&lt;/li&gt;
&lt;li&gt;Store passwords in a &lt;code&gt;.env&lt;/code&gt; file — never hardcode them in the compose file&lt;/li&gt;
&lt;li&gt;Add the &lt;code&gt;.env&lt;/code&gt; file to &lt;code&gt;.gitignore&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Set resource limits (&lt;code&gt;deploy.resources.limits&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Configure log rotation (&lt;code&gt;max-size&lt;/code&gt;, &lt;code&gt;max-file&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Keep images updated with regular &lt;code&gt;docker compose pull&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Container Registries
&lt;/h2&gt;

&lt;p&gt;Just as &lt;strong&gt;npm registry&lt;/strong&gt; stores packages, a Docker registry stores images. You build an image, push it to a registry, and anyone in the world can pull it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; A Docker registry is a storage and distribution system for Docker images. Docker Hub is the default public registry, but you can use private registries for proprietary images. Think of it as GitHub for code — but for images.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Image Naming Convention
&lt;/h3&gt;

&lt;p&gt;Every Docker image has a fully qualified name. The format is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;registry/][username/]image[:tag][@digest]

&lt;span class="c"&gt;# Examples — from simple to fully qualified:&lt;/span&gt;
ubuntu                                                        &lt;span class="c"&gt;# Docker Hub official (implicit library/)&lt;/span&gt;
nginx:1.25-alpine                                             &lt;span class="c"&gt;# Docker Hub official with tag&lt;/span&gt;
library/ubuntu:22.04                                          &lt;span class="c"&gt;# Explicit official image path&lt;/span&gt;
username/myapp:v1.0                                           &lt;span class="c"&gt;# Docker Hub user image&lt;/span&gt;

&lt;span class="c"&gt;# Cloud registries&lt;/span&gt;
ghcr.io/username/myapp:latest                                 &lt;span class="c"&gt;# GitHub Container Registry&lt;/span&gt;
123456789.dkr.ecr.us-east-1.amazonaws.com/myapp:v1.0         &lt;span class="c"&gt;# AWS ECR (region-scoped)&lt;/span&gt;
asia.gcr.io/myproject/myapp:latest                            &lt;span class="c"&gt;# Google Container Registry&lt;/span&gt;
registry.gitlab.com/group/project/myapp:1.0                   &lt;span class="c"&gt;# GitLab Registry&lt;/span&gt;

&lt;span class="c"&gt;# Digest (immutable — safer than a tag!)&lt;/span&gt;
ubuntu@sha256:45b23dee08af5e43a7fea6c4cf9c25ccf269ee113168c19722f87876677c5cb2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Image Tagging Strategy
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tag Type&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;th&gt;Use Case&lt;/th&gt;
&lt;th&gt;Stable?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;latest&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;myapp:latest&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Development&lt;/td&gt;
&lt;td&gt;No — changes!&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Semantic Version&lt;/td&gt;
&lt;td&gt;&lt;code&gt;myapp:v1.2.3&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Production releases&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Git SHA&lt;/td&gt;
&lt;td&gt;&lt;code&gt;myapp:a3f1b2c&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;CI/CD traceability&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Branch name&lt;/td&gt;
&lt;td&gt;&lt;code&gt;myapp:main&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Branch-based testing&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Date-based&lt;/td&gt;
&lt;td&gt;&lt;code&gt;myapp:20240115&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Nightly builds&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Digest&lt;/td&gt;
&lt;td&gt;&lt;code&gt;myapp@sha256:abc...&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pinned dependencies&lt;/td&gt;
&lt;td&gt;Immutable&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; &lt;strong&gt;Avoid the latest Tag!&lt;/strong&gt; Using the &lt;code&gt;latest&lt;/code&gt; tag in production is dangerous. If a new image is pushed, your deployment may update unexpectedly. Always use specific version tags in production — &lt;code&gt;myapp:v1.2.3&lt;/code&gt; or a Git SHA.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; &lt;strong&gt;Best Practice: Multiple Tags.&lt;/strong&gt; Tag a single image with multiple tags: &lt;code&gt;myapp:v1.2.3&lt;/code&gt;, &lt;code&gt;myapp:v1.2&lt;/code&gt;, &lt;code&gt;myapp:v1&lt;/code&gt;, and &lt;code&gt;myapp:latest&lt;/code&gt;. This gives users flexibility while maintaining immutable specific versions.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Docker Hub — The Default Registry
&lt;/h2&gt;

&lt;p&gt;Docker Hub is the default public registry — like npm for JavaScript packages. Start with a free account and explore private registries once you hit the limits.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Create an account at &lt;a href="https://hub.docker.com" rel="noopener noreferrer"&gt;hub.docker.com&lt;/a&gt;. Free accounts include 1 private repository and unlimited public repositories. Use access tokens rather than passwords — they are safer.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Complete Docker Hub Workflow
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Step 1: Login to Docker Hub&lt;/span&gt;
docker login
&lt;span class="c"&gt;# Username: yourname&lt;/span&gt;
&lt;span class="c"&gt;# Password: (enter password or access token)&lt;/span&gt;

&lt;span class="c"&gt;# Better: login with an access token (Settings → Security → New Access Token)&lt;/span&gt;
docker login &lt;span class="nt"&gt;-u&lt;/span&gt; username &lt;span class="nt"&gt;--password-stdin&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s2"&gt;"your_access_token_here"&lt;/span&gt;

&lt;span class="c"&gt;# Step 2: Tag your local image for Docker Hub&lt;/span&gt;
docker tag myapp username/myapp:v1.0.0
docker tag myapp username/myapp:latest

&lt;span class="c"&gt;# Step 3: Push to Docker Hub&lt;/span&gt;
docker push username/myapp:v1.0.0
docker push username/myapp:latest

&lt;span class="c"&gt;# Step 4: Pull from anywhere in the world&lt;/span&gt;
docker pull username/myapp:v1.0.0

&lt;span class="c"&gt;# Step 5: Logout (important on shared machines!)&lt;/span&gt;
docker &lt;span class="nb"&gt;logout&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Rate Limits — Important for CI/CD
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Account Type&lt;/th&gt;
&lt;th&gt;Pull Limit&lt;/th&gt;
&lt;th&gt;Time Window&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Anonymous (no login)&lt;/td&gt;
&lt;td&gt;100 pulls&lt;/td&gt;
&lt;td&gt;Per 6 hours / IP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Free account (authenticated)&lt;/td&gt;
&lt;td&gt;200 pulls&lt;/td&gt;
&lt;td&gt;Per 6 hours&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pro / Team / Business&lt;/td&gt;
&lt;td&gt;Unlimited&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Authenticate pulls in CI/CD to avoid rate limits&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DOCKER_PASSWORD&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | docker login &lt;span class="nt"&gt;-u&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DOCKER_USERNAME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--password-stdin&lt;/span&gt;

&lt;span class="c"&gt;# Check your current rate limit status&lt;/span&gt;
&lt;span class="nv"&gt;TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"https://auth.docker.io/token?service=registry.docker.io&amp;amp;scope=repository:ratelimitpreview/test:pull"&lt;/span&gt; | jq &lt;span class="nt"&gt;-r&lt;/span&gt; .token&lt;span class="si"&gt;)&lt;/span&gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;--head&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; https://registry-1.docker.io/v2/ratelimitpreview/test/manifests/latest | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; ratelimit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Rate Limit Workaround — Mirror Setup
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# /etc/docker/daemon.json — configure a Docker Hub mirror&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;nano /etc/docker/daemon.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"registry-mirrors"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"https://mirror.gcr.io"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"https://registry.docker-cn.com"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Restart Docker daemon after the config change&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl restart docker

&lt;span class="c"&gt;# Verify the mirror is active&lt;/span&gt;
docker info | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-A2&lt;/span&gt; &lt;span class="s2"&gt;"Registry Mirrors"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; &lt;strong&gt;CI/CD Rate Limit Problem.&lt;/strong&gt; If your CI/CD pipeline pulls images anonymously (no &lt;code&gt;docker login&lt;/code&gt;), rate limits are hit quickly — especially in large teams. Solution: always authenticate in CI, or mirror images to your own private registry.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; &lt;strong&gt;Use Access Tokens, Not Passwords.&lt;/strong&gt; Docker Hub Settings → Security → New Access Token. Tokens can be given specific permissions (read-only, read-write). If a token is compromised, only that token needs to be revoked — not the full account. Always use access tokens in CI/CD.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Other Registries — AWS ECR, GitHub, GCR
&lt;/h2&gt;

&lt;p&gt;Docker Hub is not the only option. If you deploy to AWS, ECR is the best choice. If you use GitHub Actions, &lt;code&gt;ghcr.io&lt;/code&gt; is a perfect fit. Here is how to use each.&lt;/p&gt;

&lt;h3&gt;
  
  
  AWS Elastic Container Registry (ECR)
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;strong&gt;When to use ECR:&lt;/strong&gt; When your application is deployed to AWS (ECS, EKS, Lambda). ECR images integrate automatically with AWS services — no extra authentication is needed when running on AWS with proper IAM roles.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Prerequisites: AWS CLI installed and configured (aws configure)&lt;/span&gt;

&lt;span class="c"&gt;# Step 1: Authenticate with ECR (generates a 12-hour token)&lt;/span&gt;
aws ecr get-login-password &lt;span class="nt"&gt;--region&lt;/span&gt; ap-south-1 | &lt;span class="se"&gt;\&lt;/span&gt;
  docker login &lt;span class="nt"&gt;--username&lt;/span&gt; AWS &lt;span class="nt"&gt;--password-stdin&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  123456789.dkr.ecr.ap-south-1.amazonaws.com

&lt;span class="c"&gt;# Step 2: Create a repository in ECR&lt;/span&gt;
aws ecr create-repository &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--repository-name&lt;/span&gt; myapp &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; ap-south-1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--image-scanning-configuration&lt;/span&gt; &lt;span class="nv"&gt;scanOnPush&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--encryption-configuration&lt;/span&gt; &lt;span class="nv"&gt;encryptionType&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;AES256

&lt;span class="c"&gt;# Step 3: Tag the image for ECR&lt;/span&gt;
&lt;span class="nv"&gt;ECR_URI&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"123456789.dkr.ecr.ap-south-1.amazonaws.com"&lt;/span&gt;
docker tag myapp:latest &lt;span class="nv"&gt;$ECR_URI&lt;/span&gt;/myapp:latest
docker tag myapp:latest &lt;span class="nv"&gt;$ECR_URI&lt;/span&gt;/myapp:v1.0.0

&lt;span class="c"&gt;# Step 4: Push&lt;/span&gt;
docker push &lt;span class="nv"&gt;$ECR_URI&lt;/span&gt;/myapp:latest
docker push &lt;span class="nv"&gt;$ECR_URI&lt;/span&gt;/myapp:v1.0.0

&lt;span class="c"&gt;# Step 5: Pull (from any authenticated source)&lt;/span&gt;
docker pull &lt;span class="nv"&gt;$ECR_URI&lt;/span&gt;/myapp:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# ECR Lifecycle Policy — auto-delete old images (save storage costs!)&lt;/span&gt;
aws ecr put-lifecycle-policy &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--repository-name&lt;/span&gt; myapp &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--lifecycle-policy&lt;/span&gt; &lt;span class="s1"&gt;'{
    "rules": [{
      "rulePriority": 1,
      "description": "Keep only last 10 images",
      "selection": {
        "tagStatus": "any",
        "countType": "imageCountMoreThan",
        "countNumber": 10
      },
      "action": {"type": "expire"}
    }]
  }'&lt;/span&gt;

&lt;span class="c"&gt;# List images in ECR repository&lt;/span&gt;
aws ecr describe-images &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--repository-name&lt;/span&gt; myapp &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; ap-south-1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s1"&gt;'sort_by(imageDetails, &amp;amp;imagePushedAt)[-5:]'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  GitHub Container Registry (ghcr.io)
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;GITHUB_TOKEN&lt;/code&gt; is automatically available in GitHub Actions — perfect integration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Step 1: Create a Personal Access Token (PAT)&lt;/span&gt;
&lt;span class="c"&gt;# GitHub → Settings → Developer Settings → Personal Access Tokens&lt;/span&gt;
&lt;span class="c"&gt;# Required scopes: read:packages, write:packages, delete:packages&lt;/span&gt;

&lt;span class="c"&gt;# Step 2: Login to ghcr.io&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$GITHUB_TOKEN&lt;/span&gt; | docker login ghcr.io &lt;span class="nt"&gt;-u&lt;/span&gt; USERNAME &lt;span class="nt"&gt;--password-stdin&lt;/span&gt;

&lt;span class="c"&gt;# Step 3: Tag and push&lt;/span&gt;
docker tag myapp ghcr.io/username/myapp:latest
docker push ghcr.io/username/myapp:latest

&lt;span class="c"&gt;# Make the image public (default is private)&lt;/span&gt;
&lt;span class="c"&gt;# GitHub → Package → Package Settings → Change Visibility&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# GitHub Actions — push to ghcr.io (automatic authentication!)&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build and Push&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;read&lt;/span&gt;
      &lt;span class="na"&gt;packages&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt;        &lt;span class="c1"&gt;# Required for ghcr.io!&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Login to GitHub Container Registry&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/login-action@v3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;registry&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ghcr.io&lt;/span&gt;
          &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.actor }}&lt;/span&gt;
          &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;  &lt;span class="c1"&gt;# Auto-available!&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build and Push&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/build-push-action@v5&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
          &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
          &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ghcr.io/${{ github.repository }}/myapp:latest&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Google Container Registry / Artifact Registry
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Authenticate gcloud with Docker&lt;/span&gt;
gcloud auth configure-docker asia.gcr.io

&lt;span class="c"&gt;# New Artifact Registry (recommended over GCR)&lt;/span&gt;
gcloud auth configure-docker asia-south1-docker.pkg.dev

&lt;span class="c"&gt;# Tag and push&lt;/span&gt;
docker tag myapp asia.gcr.io/my-project/myapp:v1.0
docker push asia.gcr.io/my-project/myapp:v1.0

&lt;span class="c"&gt;# Artifact Registry (newer, more features)&lt;/span&gt;
docker tag myapp asia-south1-docker.pkg.dev/my-project/my-repo/myapp:v1.0
docker push asia-south1-docker.pkg.dev/my-project/my-repo/myapp:v1.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Registry Comparison
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Registry&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;th&gt;Best For&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Docker Hub&lt;/td&gt;
&lt;td&gt;Cloud&lt;/td&gt;
&lt;td&gt;Free (limited) / Paid&lt;/td&gt;
&lt;td&gt;Open source, small teams&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AWS ECR&lt;/td&gt;
&lt;td&gt;Cloud&lt;/td&gt;
&lt;td&gt;Per storage + transfer&lt;/td&gt;
&lt;td&gt;AWS deployments&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Google GCR/AR&lt;/td&gt;
&lt;td&gt;Cloud&lt;/td&gt;
&lt;td&gt;Per storage + transfer&lt;/td&gt;
&lt;td&gt;GCP deployments&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GitHub Packages&lt;/td&gt;
&lt;td&gt;Cloud&lt;/td&gt;
&lt;td&gt;Free for public&lt;/td&gt;
&lt;td&gt;Open source + CI/CD&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Harbor&lt;/td&gt;
&lt;td&gt;Self-hosted&lt;/td&gt;
&lt;td&gt;Free (infra cost)&lt;/td&gt;
&lt;td&gt;Enterprise, full control&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nexus Repository&lt;/td&gt;
&lt;td&gt;Self-hosted&lt;/td&gt;
&lt;td&gt;Free/Commercial&lt;/td&gt;
&lt;td&gt;Enterprise artifact management&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Deploying to AWS → use ECR (pick the Region closest to your users). Using GitHub Actions → use ghcr.io (free for public repos). Startup or open source → start with the Docker Hub free tier.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Pushing and Pulling Workflows — CI/CD Integration
&lt;/h2&gt;

&lt;p&gt;Integrating the registry workflow with CI/CD is the core of production-ready DevOps. Here is a complete, real-world workflow for a React application.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;strong&gt;Workflow Overview:&lt;/strong&gt; Build → Tag (with version + Git SHA) → Authenticate → Push to Registry → Verify. Every step matters. Tagging with the Git SHA lets you know exactly which commit is running in production.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Complete Registry Push Workflow
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="c"&gt;# Full CI/CD workflow for a React app&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;  &lt;span class="c"&gt;# Exit on any error&lt;/span&gt;

&lt;span class="nv"&gt;IMAGE_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"myreactapp"&lt;/span&gt;
&lt;span class="nv"&gt;REGISTRY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"123456789.dkr.ecr.ap-south-1.amazonaws.com"&lt;/span&gt;

&lt;span class="c"&gt;# Generate meaningful version tags&lt;/span&gt;
&lt;span class="nv"&gt;VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git describe &lt;span class="nt"&gt;--tags&lt;/span&gt; &lt;span class="nt"&gt;--always&lt;/span&gt; &lt;span class="nt"&gt;--dirty&lt;/span&gt; 2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"dev"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;GIT_SHA&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git rev-parse &lt;span class="nt"&gt;--short&lt;/span&gt; HEAD&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;DATE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%Y%m%d&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;BRANCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git rev-parse &lt;span class="nt"&gt;--abbrev-ref&lt;/span&gt; HEAD | &lt;span class="nb"&gt;tr&lt;/span&gt; / -&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Building version: &lt;/span&gt;&lt;span class="nv"&gt;$VERSION&lt;/span&gt;&lt;span class="s2"&gt; (SHA: &lt;/span&gt;&lt;span class="nv"&gt;$GIT_SHA&lt;/span&gt;&lt;span class="s2"&gt;)"&lt;/span&gt;

&lt;span class="c"&gt;# Step 1: Build with build args for traceability&lt;/span&gt;
docker build &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--build-arg&lt;/span&gt; &lt;span class="nv"&gt;BUILD_DATE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$DATE&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--build-arg&lt;/span&gt; &lt;span class="nv"&gt;GIT_COMMIT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$GIT_SHA&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--build-arg&lt;/span&gt; &lt;span class="nv"&gt;VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$VERSION&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-t&lt;/span&gt; &lt;span class="nv"&gt;$IMAGE_NAME&lt;/span&gt;:&lt;span class="nv"&gt;$VERSION&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-t&lt;/span&gt; &lt;span class="nv"&gt;$IMAGE_NAME&lt;/span&gt;:&lt;span class="nv"&gt;$GIT_SHA&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-t&lt;/span&gt; &lt;span class="nv"&gt;$IMAGE_NAME&lt;/span&gt;:latest &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Build complete"&lt;/span&gt;

&lt;span class="c"&gt;# Step 2: Authenticate with ECR&lt;/span&gt;
aws ecr get-login-password &lt;span class="nt"&gt;--region&lt;/span&gt; ap-south-1 | &lt;span class="se"&gt;\&lt;/span&gt;
  docker login &lt;span class="nt"&gt;--username&lt;/span&gt; AWS &lt;span class="nt"&gt;--password-stdin&lt;/span&gt; &lt;span class="nv"&gt;$REGISTRY&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Authenticated with ECR"&lt;/span&gt;

&lt;span class="c"&gt;# Step 3: Tag all versions for registry&lt;/span&gt;
docker tag &lt;span class="nv"&gt;$IMAGE_NAME&lt;/span&gt;:&lt;span class="nv"&gt;$VERSION&lt;/span&gt; &lt;span class="nv"&gt;$REGISTRY&lt;/span&gt;/&lt;span class="nv"&gt;$IMAGE_NAME&lt;/span&gt;:&lt;span class="nv"&gt;$VERSION&lt;/span&gt;
docker tag &lt;span class="nv"&gt;$IMAGE_NAME&lt;/span&gt;:&lt;span class="nv"&gt;$GIT_SHA&lt;/span&gt; &lt;span class="nv"&gt;$REGISTRY&lt;/span&gt;/&lt;span class="nv"&gt;$IMAGE_NAME&lt;/span&gt;:&lt;span class="nv"&gt;$GIT_SHA&lt;/span&gt;
docker tag &lt;span class="nv"&gt;$IMAGE_NAME&lt;/span&gt;:latest &lt;span class="nv"&gt;$REGISTRY&lt;/span&gt;/&lt;span class="nv"&gt;$IMAGE_NAME&lt;/span&gt;:latest

&lt;span class="c"&gt;# Step 4: Push all tags&lt;/span&gt;
docker push &lt;span class="nv"&gt;$REGISTRY&lt;/span&gt;/&lt;span class="nv"&gt;$IMAGE_NAME&lt;/span&gt;:&lt;span class="nv"&gt;$VERSION&lt;/span&gt;
docker push &lt;span class="nv"&gt;$REGISTRY&lt;/span&gt;/&lt;span class="nv"&gt;$IMAGE_NAME&lt;/span&gt;:&lt;span class="nv"&gt;$GIT_SHA&lt;/span&gt;
docker push &lt;span class="nv"&gt;$REGISTRY&lt;/span&gt;/&lt;span class="nv"&gt;$IMAGE_NAME&lt;/span&gt;:latest
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"All tags pushed"&lt;/span&gt;

&lt;span class="c"&gt;# Step 5: Verify push was successful&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Recent images in ECR:"&lt;/span&gt;
aws ecr describe-images &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--repository-name&lt;/span&gt; &lt;span class="nv"&gt;$IMAGE_NAME&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; ap-south-1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s1"&gt;'sort_by(imageDetails, &amp;amp;imagePushedAt)[-5:].{Tags:imageTags,Size:imageSizeInBytes,Pushed:imagePushedAt}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; table
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  GitHub Actions — Full CI/CD Pipeline
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build, Scan, and Push&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;REGISTRY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;123456789.dkr.ecr.ap-south-1.amazonaws.com&lt;/span&gt;
  &lt;span class="na"&gt;IMAGE_NAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myreactapp&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build-and-push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout code&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;fetch-depth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;  &lt;span class="c1"&gt;# Full history for git describe&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Configure AWS credentials&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aws-actions/configure-aws-credentials@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;aws-access-key-id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.AWS_ACCESS_KEY_ID }}&lt;/span&gt;
          &lt;span class="na"&gt;aws-secret-access-key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.AWS_SECRET_ACCESS_KEY }}&lt;/span&gt;
          &lt;span class="na"&gt;aws-region&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ap-south-1&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Login to Amazon ECR&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aws-actions/amazon-ecr-login@v2&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set up Docker Buildx&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/setup-buildx-action@v3&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Generate image metadata&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;meta&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/metadata-action@v5&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;images&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}&lt;/span&gt;
          &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;type=sha,prefix=sha-&lt;/span&gt;
            &lt;span class="s"&gt;type=ref,event=branch&lt;/span&gt;
            &lt;span class="s"&gt;type=semver,pattern={{version}}&lt;/span&gt;
            &lt;span class="s"&gt;type=raw,value=latest,enable={{is_default_branch}}&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build and push&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/build-push-action@v5&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
          &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.event_name == 'push' }}&lt;/span&gt;  &lt;span class="c1"&gt;# Only push on merge to main&lt;/span&gt;
          &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ steps.meta.outputs.tags }}&lt;/span&gt;
          &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ steps.meta.outputs.labels }}&lt;/span&gt;
          &lt;span class="na"&gt;cache-from&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;type=gha&lt;/span&gt;          &lt;span class="c1"&gt;# GitHub Actions cache&lt;/span&gt;
          &lt;span class="na"&gt;cache-to&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;type=gha,mode=max&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; &lt;strong&gt;Do Not Push on Pull Requests!&lt;/strong&gt; Pull request builds should only build and test — do not push images. Push only when merging to main/master. That is why &lt;code&gt;push: ${{ github.event_name == 'push' }}&lt;/code&gt; is used.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; &lt;strong&gt;Speed Up Builds with Cache.&lt;/strong&gt; &lt;code&gt;cache-from: type=gha&lt;/code&gt; and &lt;code&gt;cache-to: type=gha,mode=max&lt;/code&gt; use GitHub Actions cache. The first build will be slow; subsequent builds are significantly faster — especially when dependencies have not changed.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Container Security Fundamentals — Why It Matters
&lt;/h2&gt;

&lt;p&gt;Docker containers share the host kernel. This means — &lt;strong&gt;if a container escapes, an attacker can reach the host machine&lt;/strong&gt;. Do not treat security as an afterthought; you must think about it while writing your Dockerfile.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; Container escape is real. In 2019, a runc vulnerability (CVE-2019-5736) allowed attackers to access the host machine from inside a container. In 2020, a Docker Desktop vulnerability followed. This is not theoretical — production systems have been compromised. Always follow security fundamentals.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Common Container Vulnerabilities
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Vulnerability&lt;/th&gt;
&lt;th&gt;Risk Level&lt;/th&gt;
&lt;th&gt;Impact&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Running as root&lt;/td&gt;
&lt;td&gt;Critical&lt;/td&gt;
&lt;td&gt;Container escape, host access&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Outdated base images (CVEs)&lt;/td&gt;
&lt;td&gt;Critical&lt;/td&gt;
&lt;td&gt;Known exploits in production&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Hardcoded secrets in image&lt;/td&gt;
&lt;td&gt;Critical&lt;/td&gt;
&lt;td&gt;Credential theft via docker history&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Privileged mode&lt;/td&gt;
&lt;td&gt;Critical&lt;/td&gt;
&lt;td&gt;Full host access&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Excessive capabilities&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Privilege escalation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;No resource limits&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Denial of Service (fork bomb)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;Unscanned images&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Unknown vulnerabilities&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;Docker socket exposed&lt;/td&gt;
&lt;td&gt;Critical&lt;/td&gt;
&lt;td&gt;Complete host compromise&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Real Attack Scenarios
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Scenario 1: Secret leakage via docker history&lt;/span&gt;
docker &lt;span class="nb"&gt;history &lt;/span&gt;myapp:latest &lt;span class="nt"&gt;--no-trunc&lt;/span&gt;
&lt;span class="c"&gt;# IMAGE     CREATED   CREATED BY&lt;/span&gt;
&lt;span class="c"&gt;# abc123    ...       /bin/sh -c export API_KEY=sk-prod-secret123 &amp;amp;&amp;amp; node setup.js&lt;/span&gt;
&lt;span class="c"&gt;#                     ^^^^ VISIBLE TO ANYONE who pulls the image!&lt;/span&gt;

&lt;span class="c"&gt;# Scenario 2: Crypto mining via compromised public image&lt;/span&gt;
&lt;span class="c"&gt;# Attacker pushes malicious "ubuntu:latest" to a similarly-named registry&lt;/span&gt;
&lt;span class="c"&gt;# Your CI/CD pulls it, runs a crypto miner in your cloud (you pay the bill!)&lt;/span&gt;

&lt;span class="c"&gt;# Scenario 3: Docker socket exposure&lt;/span&gt;
docker run &lt;span class="nt"&gt;-v&lt;/span&gt; /var/run/docker.sock:/var/run/docker.sock myapp
&lt;span class="c"&gt;# Attacker inside container can now:&lt;/span&gt;
docker run &lt;span class="nt"&gt;-v&lt;/span&gt; /:/host alpine &lt;span class="nb"&gt;chroot&lt;/span&gt; /host  &lt;span class="c"&gt;# Read/write host filesystem!&lt;/span&gt;

&lt;span class="c"&gt;# Scenario 4: Privileged container escape&lt;/span&gt;
docker run &lt;span class="nt"&gt;--privileged&lt;/span&gt; alpine
&lt;span class="c"&gt;# Inside container:&lt;/span&gt;
mount /dev/sda1 /mnt  &lt;span class="c"&gt;# Mount host disk&lt;/span&gt;
&lt;span class="nb"&gt;chroot&lt;/span&gt; /mnt           &lt;span class="c"&gt;# Root access to host!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; Never mount &lt;code&gt;/var/run/docker.sock&lt;/code&gt; into a container in production unless absolutely necessary (such as for CI agents). Any container that can access the Docker socket effectively has root access to the entire machine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Container security is layered: secure Dockerfile → runtime security flags → network policies → image scanning → secrets management → monitoring. If one layer fails, the next catches it. This is the "defense in depth" model.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Secure Dockerfile Practices — 20+ Best Practices
&lt;/h2&gt;

&lt;p&gt;Security starts at build time. An insecure Dockerfile cannot be fixed at runtime. Follow these best practices for production-ready Dockerfiles.&lt;/p&gt;

&lt;h3&gt;
  
  
  Complete Secure Dockerfile Template
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# ============================================&lt;/span&gt;
&lt;span class="c"&gt;# SECURE DOCKERFILE TEMPLATE — React/Node App&lt;/span&gt;
&lt;span class="c"&gt;# ============================================&lt;/span&gt;

&lt;span class="c"&gt;# Best Practice #1: Use specific versions — never "latest" or just "node"&lt;/span&gt;
&lt;span class="c"&gt;# The "latest" tag can change, breaking your build&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:18.19.0-alpine3.19&lt;/span&gt;

&lt;span class="c"&gt;# Best Practice #2: Use minimal base images&lt;/span&gt;
&lt;span class="c"&gt;# Security risk order (most to least): ubuntu &amp;gt; debian &amp;gt; debian-slim &amp;gt; alpine &amp;gt; distroless&lt;/span&gt;
&lt;span class="c"&gt;# Alpine has 98% fewer packages = smaller attack surface&lt;/span&gt;

&lt;span class="c"&gt;# Best Practice #3: Update packages in base image (apply security patches)&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apk update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    apk upgrade &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /var/cache/apk/&lt;span class="k"&gt;*&lt;/span&gt;
&lt;span class="c"&gt;# Note: Alpine uses apk; Debian/Ubuntu uses apt-get&lt;/span&gt;

&lt;span class="c"&gt;# Best Practice #4: Create a non-root user EARLY&lt;/span&gt;
&lt;span class="c"&gt;# Never run the app as root!&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;addgroup &lt;span class="nt"&gt;--system&lt;/span&gt; &lt;span class="nt"&gt;--gid&lt;/span&gt; 1001 appgroup &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    adduser &lt;span class="nt"&gt;--system&lt;/span&gt; &lt;span class="nt"&gt;--uid&lt;/span&gt; 1001 &lt;span class="nt"&gt;--ingroup&lt;/span&gt; appgroup appuser

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="c"&gt;# Best Practice #5: Copy dependency files FIRST (caching + security)&lt;/span&gt;
&lt;span class="c"&gt;# Set proper ownership during COPY with --chown&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --chown=appuser:appgroup package*.json ./&lt;/span&gt;

&lt;span class="c"&gt;# Best Practice #6: Install ONLY production dependencies&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm ci &lt;span class="nt"&gt;--only&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;production &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    npm cache clean &lt;span class="nt"&gt;--force&lt;/span&gt;
&lt;span class="c"&gt;# npm ci = exact versions from package-lock.json (more reproducible)&lt;/span&gt;

&lt;span class="c"&gt;# Best Practice #7: Copy app source with proper ownership&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --chown=appuser:appgroup . .&lt;/span&gt;

&lt;span class="c"&gt;# Best Practice #8: Build (if needed) while still root for permissions&lt;/span&gt;
&lt;span class="c"&gt;# Then switch to non-root for runtime&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm run build

&lt;span class="c"&gt;# Best Practice #9: Remove dev files and build artifacts&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; src/ tests/ &lt;span class="k"&gt;*&lt;/span&gt;.test.js .env.&lt;span class="k"&gt;*&lt;/span&gt; coverage/

&lt;span class="c"&gt;# Best Practice #10: Switch to non-root user — everything below runs as appuser&lt;/span&gt;
&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; appuser&lt;/span&gt;

&lt;span class="c"&gt;# Best Practice #11: Expose ONLY required ports (documentation, not enforcement)&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 3000&lt;/span&gt;

&lt;span class="c"&gt;# Best Practice #12: Add HEALTHCHECK — K8s/Docker knows if app is actually working&lt;/span&gt;
&lt;span class="k"&gt;HEALTHCHECK&lt;/span&gt;&lt;span class="s"&gt; --interval=30s --timeout=5s --start-period=30s --retries=3 \&lt;/span&gt;
  CMD node -e "require('http').get('http://localhost:3000/health', r =&amp;gt; process.exit(r.statusCode===200?0:1))" || exit 1

&lt;span class="c"&gt;# Best Practice #13: Use exec form CMD (not shell form)&lt;/span&gt;
&lt;span class="c"&gt;# Shell form: CMD "node server.js" → /bin/sh -c "node server.js" (signals don't propagate!)&lt;/span&gt;
&lt;span class="c"&gt;# Exec form: CMD ["node", "server.js"] → direct process (signals work correctly)&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["node", "server.js"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Security Anti-Patterns — Never Do This
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# ❌ DANGEROUS: Hardcoded secrets visible in docker history!&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; DATABASE_PASSWORD=mysupersecretpassword&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; AWS_SECRET_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY&lt;/span&gt;

&lt;span class="c"&gt;# ❌ DANGEROUS: Even in RUN, it stays in layer history&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;abc123secret &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm run setup

&lt;span class="c"&gt;# ❌ BAD: Copying entire directory without .dockerignore&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="c"&gt;# This might include: .env, .git/, node_modules/, passwords.txt, etc.&lt;/span&gt;

&lt;span class="c"&gt;# ❌ BAD: Using latest tag&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:latest  # Which version? Will break unexpectedly!&lt;/span&gt;

&lt;span class="c"&gt;# ❌ BAD: Running as root (default if no USER instruction)&lt;/span&gt;
&lt;span class="c"&gt;# root inside container → if compromised, easier to escalate&lt;/span&gt;

&lt;span class="c"&gt;# ❌ BAD: Adding unnecessary packages&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; curl wget vim git build-essential
&lt;span class="c"&gt;# More packages = larger attack surface&lt;/span&gt;

&lt;span class="c"&gt;# ❌ BAD: Ignoring package-lock.json&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;  &lt;span class="c"&gt;# Might install different versions each time!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  .dockerignore — First Line of Defense
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Always maintain a comprehensive .dockerignore!

# Secrets and config
.env
.env.*
.env.local
.env.production
secrets/
*.pem
*.key
*.cert

# Development artifacts
node_modules/
npm-debug.log*
yarn-error.log

# Git history (can contain old secrets!)
.git/
.gitignore

# Tests and coverage (not needed in production image)
tests/
__tests__/
*.test.js
*.spec.js
coverage/
.nyc_output/

# Documentation
README.md
docs/
*.md

# IDE files
.vscode/
.idea/
*.swp

# CI/CD configs (not needed in image)
.github/
.gitlab-ci.yml
Jenkinsfile

# Build artifacts (will be regenerated)
dist/
build/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; &lt;code&gt;.env&lt;/code&gt; MUST be in &lt;code&gt;.dockerignore&lt;/code&gt;. If you use &lt;code&gt;COPY . .&lt;/code&gt; and &lt;code&gt;.env&lt;/code&gt; is not excluded, all your secrets end up in the image. Anyone can see them via &lt;code&gt;docker history myapp&lt;/code&gt; or image inspection. Real data breaches have happened this way.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Multi-stage builds give you both security and smaller image sizes. Build tools (gcc, make, npm, pip) do not belong in a production image. Copy only the runtime artifacts into the final stage — no build tools means a smaller attack surface and 60–80% smaller images.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Google's distroless images contain no shell, no package manager, and no utilities — only the app runtime. An attacker cannot even open a shell. Use &lt;code&gt;gcr.io/distroless/nodejs18-debian11&lt;/code&gt;. The trade-off is harder debugging — use the debug variant for that purpose.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Runtime Security — Sandboxing Containers
&lt;/h2&gt;

&lt;p&gt;After building a secure Dockerfile, apply security flags at runtime. These flags keep the container in a tight sandbox — even if the container is compromised, the damage is contained.&lt;/p&gt;

&lt;h3&gt;
  
  
  Runtime Security Flags
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Drop ALL capabilities, add back only what's needed&lt;/span&gt;
&lt;span class="c"&gt;# Linux capabilities are specific privileges that root has&lt;/span&gt;
docker run &lt;span class="nt"&gt;--cap-drop&lt;/span&gt; ALL &lt;span class="nt"&gt;--cap-add&lt;/span&gt; NET_BIND_SERVICE nginx
&lt;span class="c"&gt;# Drops 38 capabilities and adds back only port binding &amp;lt; 1024&lt;/span&gt;

&lt;span class="c"&gt;# 2. Read-only filesystem — prevent tampering&lt;/span&gt;
docker run &lt;span class="nt"&gt;--read-only&lt;/span&gt; &lt;span class="nt"&gt;--tmpfs&lt;/span&gt; /tmp &lt;span class="nt"&gt;--tmpfs&lt;/span&gt; /var/run myapp
&lt;span class="c"&gt;# --read-only: container filesystem is read-only&lt;/span&gt;
&lt;span class="c"&gt;# --tmpfs /tmp: in-memory temp storage (data lost on restart)&lt;/span&gt;

&lt;span class="c"&gt;# 3. No new privileges — prevent sudo/setuid escalation&lt;/span&gt;
docker run &lt;span class="nt"&gt;--security-opt&lt;/span&gt; no-new-privileges myapp
&lt;span class="c"&gt;# Even if a setuid binary exists, it cannot escalate privileges&lt;/span&gt;

&lt;span class="c"&gt;# 4. Custom seccomp profile — limit system calls&lt;/span&gt;
docker run &lt;span class="nt"&gt;--security-opt&lt;/span&gt; &lt;span class="nv"&gt;seccomp&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/path/to/seccomp-profile.json myapp
&lt;span class="c"&gt;# Default Docker seccomp blocks 44 of 300+ syscalls&lt;/span&gt;
&lt;span class="c"&gt;# A custom profile can be more restrictive&lt;/span&gt;

&lt;span class="c"&gt;# 5. AppArmor profile — Mandatory Access Control&lt;/span&gt;
docker run &lt;span class="nt"&gt;--security-opt&lt;/span&gt; &lt;span class="nv"&gt;apparmor&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;docker-default myapp

&lt;span class="c"&gt;# 6. Resource limits — prevent Denial of Service&lt;/span&gt;
docker run &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--memory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;512m &lt;span class="se"&gt;\ &lt;/span&gt;          &lt;span class="c"&gt;# Max RAM&lt;/span&gt;
  &lt;span class="nt"&gt;--memory-swap&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;512m &lt;span class="se"&gt;\ &lt;/span&gt;     &lt;span class="c"&gt;# No swap (set equal to memory)&lt;/span&gt;
  &lt;span class="nt"&gt;--cpus&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0.5 &lt;span class="se"&gt;\ &lt;/span&gt;             &lt;span class="c"&gt;# Max 0.5 CPU cores&lt;/span&gt;
  &lt;span class="nt"&gt;--pids-limit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;100 &lt;span class="se"&gt;\ &lt;/span&gt;       &lt;span class="c"&gt;# Max 100 processes (prevents fork bombs!)&lt;/span&gt;
  &lt;span class="nt"&gt;--ulimit&lt;/span&gt; &lt;span class="nv"&gt;nofile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1024:1024 &lt;span class="se"&gt;\ &lt;/span&gt; &lt;span class="c"&gt;# Max open file descriptors&lt;/span&gt;
  myapp

&lt;span class="c"&gt;# 7. NEVER use --privileged in production!&lt;/span&gt;
&lt;span class="c"&gt;# docker run --privileged myapp  ← Gives container host-level access!&lt;/span&gt;

&lt;span class="c"&gt;# Complete secure run command&lt;/span&gt;
docker run &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--cap-drop&lt;/span&gt; ALL &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--cap-add&lt;/span&gt; NET_BIND_SERVICE &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--read-only&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--tmpfs&lt;/span&gt; /tmp &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--security-opt&lt;/span&gt; no-new-privileges &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--memory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;512m &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--cpus&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0.5 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--pids-limit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;100 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--user&lt;/span&gt; 1001:1001 &lt;span class="se"&gt;\&lt;/span&gt;
  myapp:v1.0.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Linux Capabilities — What to Grant and What to Deny
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Capability&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;th&gt;Use in Production?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;NET_BIND_SERVICE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Bind ports &amp;lt; 1024 (80, 443)&lt;/td&gt;
&lt;td&gt;Only if port &amp;lt; 1024&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;NET_ADMIN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Configure networking&lt;/td&gt;
&lt;td&gt;Only for network tools&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CHOWN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Change file ownership&lt;/td&gt;
&lt;td&gt;Very rarely&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DAC_OVERRIDE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Bypass file permissions&lt;/td&gt;
&lt;td&gt;Never&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;FOWNER&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Bypass owner restrictions&lt;/td&gt;
&lt;td&gt;Never&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SYS_ADMIN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Broad system admin access&lt;/td&gt;
&lt;td&gt;NEVER (equivalent to root!)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SYS_PTRACE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Process tracing (debuggers)&lt;/td&gt;
&lt;td&gt;Never in production&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;KILL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Send signals to processes&lt;/td&gt;
&lt;td&gt;Rarely needed&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  User Namespace Remapping (Advanced)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;/etc/docker/daemon.json&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;—&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;User&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;namespace&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;remapping&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Container&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;root&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;(UID&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;becomes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;an&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;unprivileged&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;user&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;host!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"userns-remap"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"default"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;After&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;this:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;sudo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;systemctl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;restart&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;docker&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Container&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;root&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;→&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;UID&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100000&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;(non-privileged)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; Never use &lt;code&gt;--privileged&lt;/code&gt; in production. The &lt;code&gt;--privileged&lt;/code&gt; flag effectively makes the container root on the host, bypassing the security context entirely. If any tutorial asks you to use &lt;code&gt;--privileged&lt;/code&gt; without explaining why, that tutorial is wrong. Use it only for specific, well-understood cases such as certain CI/CD systems.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Docker's default seccomp profile already blocks 44+ dangerous syscalls. To tighten further, create a custom profile. Docker's documentation provides the default profile as a starting point.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Image Scanning — Finding CVEs Before Production
&lt;/h2&gt;

&lt;p&gt;Your image is built and pushed — but does it contain known vulnerabilities? Image scanning tools automatically check for CVEs (Common Vulnerabilities and Exposures) in your base image and dependencies.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; A CVE (Common Vulnerability and Exposure) is the unique identifier for a publicly known security vulnerability. For example, &lt;code&gt;CVE-2021-44228&lt;/code&gt; was the Log4Shell vulnerability. A scanner compares your image's packages against a CVE database.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Trivy — Most Popular Free Scanner
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install Trivy (Linux/macOS)&lt;/span&gt;
curl &lt;span class="nt"&gt;-sfL&lt;/span&gt; https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | &lt;span class="se"&gt;\&lt;/span&gt;
  sh &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;-b&lt;/span&gt; /usr/local/bin

&lt;span class="c"&gt;# macOS via Homebrew&lt;/span&gt;
brew &lt;span class="nb"&gt;install &lt;/span&gt;trivy

&lt;span class="c"&gt;# Basic image scan&lt;/span&gt;
trivy image nginx:latest

&lt;span class="c"&gt;# Scan with severity filter — only show HIGH and CRITICAL&lt;/span&gt;
trivy image &lt;span class="nt"&gt;--severity&lt;/span&gt; HIGH,CRITICAL nginx:latest

&lt;span class="c"&gt;# Fail CI/CD if CRITICAL vulnerabilities found (exit code 1)&lt;/span&gt;
trivy image &lt;span class="nt"&gt;--exit-code&lt;/span&gt; 1 &lt;span class="nt"&gt;--severity&lt;/span&gt; CRITICAL myapp:latest

&lt;span class="c"&gt;# Scan your local Dockerfile for misconfigurations&lt;/span&gt;
trivy config ./Dockerfile

&lt;span class="c"&gt;# Scan docker-compose.yml for security issues&lt;/span&gt;
trivy config ./docker-compose.yml

&lt;span class="c"&gt;# Output as JSON for automated processing&lt;/span&gt;
trivy image &lt;span class="nt"&gt;--format&lt;/span&gt; json &lt;span class="nt"&gt;--output&lt;/span&gt; results.json myapp:latest

&lt;span class="c"&gt;# Scan a GitHub repository (without cloning)&lt;/span&gt;
trivy repo https://github.com/user/myrepo

&lt;span class="c"&gt;# Generate SBOM (Software Bill of Materials)&lt;/span&gt;
trivy image &lt;span class="nt"&gt;--format&lt;/span&gt; cyclonedx &lt;span class="nt"&gt;--output&lt;/span&gt; sbom.json myapp:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# GitHub Actions — Trivy scan in CI/CD&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run Trivy vulnerability scanner&lt;/span&gt;
  &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aquasecurity/trivy-action@master&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image-ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myapp:latest&lt;/span&gt;
    &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sarif&lt;/span&gt;
    &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;trivy-results.sarif&lt;/span&gt;
    &lt;span class="na"&gt;severity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CRITICAL,HIGH&lt;/span&gt;
    &lt;span class="na"&gt;exit-code&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;  &lt;span class="c1"&gt;# Fail build on critical issues&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Upload Trivy results to GitHub Security tab&lt;/span&gt;
  &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;github/codeql-action/upload-sarif@v3&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;sarif_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;trivy-results.sarif&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Docker Scout — Built-in Scanner
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Docker Scout (built-in since Docker Desktop 4.17)&lt;/span&gt;

&lt;span class="c"&gt;# Quick vulnerability overview&lt;/span&gt;
docker scout quickview myapp:latest

&lt;span class="c"&gt;# Detailed CVE list&lt;/span&gt;
docker scout cves myapp:latest

&lt;span class="c"&gt;# Filter by severity&lt;/span&gt;
docker scout cves &lt;span class="nt"&gt;--only-severity&lt;/span&gt; critical myapp:latest

&lt;span class="c"&gt;# Compare two versions (what changed?)&lt;/span&gt;
docker scout compare &lt;span class="nt"&gt;--to&lt;/span&gt; myapp:v1.0 myapp:v2.0

&lt;span class="c"&gt;# Get recommendations (what base image to use?)&lt;/span&gt;
docker scout recommendations myapp:latest

&lt;span class="c"&gt;# Local filesystem scan&lt;/span&gt;
docker scout cves fs://./  &lt;span class="c"&gt;# Scan current directory&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Snyk — Enterprise Scanner
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install Snyk CLI&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; snyk

&lt;span class="c"&gt;# Authenticate (free account at snyk.io)&lt;/span&gt;
snyk auth

&lt;span class="c"&gt;# Scan Docker image&lt;/span&gt;
snyk container &lt;span class="nb"&gt;test &lt;/span&gt;nginx:latest

&lt;span class="c"&gt;# Scan with fail threshold&lt;/span&gt;
snyk container &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;--severity-threshold&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;high nginx:latest

&lt;span class="c"&gt;# Monitor over time (track vulnerabilities as they are discovered)&lt;/span&gt;
snyk container monitor nginx:latest

&lt;span class="c"&gt;# Snyk in CI/CD (GitHub Actions)&lt;/span&gt;
- name: Run Snyk to check Docker image &lt;span class="k"&gt;for &lt;/span&gt;vulnerabilities
  uses: snyk/actions/docker@master
  &lt;span class="nb"&gt;env&lt;/span&gt;:
    SNYK_TOKEN: &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="p"&gt;{ secrets.SNYK_TOKEN &lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
  with:
    image: myapp:latest
    args: &lt;span class="nt"&gt;--severity-threshold&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;high
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; For development: use Docker Scout (built-in, no setup). For CI/CD: use Trivy (free, fast, no registration). For enterprise: use Snyk or Aqua Security (paid, more features). ECR users: enable "Scan on Push" in repository settings — it is automatic and free.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Achieving zero vulnerabilities is an unrealistic goal. Having LOW/MEDIUM vulnerabilities in production images is normal — fixing everything is not feasible. Focus on: always fix CRITICAL, fix or document exceptions for HIGH, address MEDIUM only if exploitable. False positives also occur — triage carefully.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Secrets Management — Never Bake Secrets Into Images
&lt;/h2&gt;

&lt;p&gt;The most common mistake in container security is hardcoding secrets (passwords, API keys, tokens) into Docker images. Once an image is pushed to a registry, &lt;strong&gt;anyone who pulls it can inspect the secrets&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; &lt;code&gt;docker history&lt;/code&gt; is a secret leakage tool. Every Dockerfile instruction creates an image layer. &lt;code&gt;ENV API_KEY=secret&lt;/code&gt; is permanently baked into that layer — even if you unset it later. Anyone can see it via &lt;code&gt;docker history --no-trunc myapp&lt;/code&gt;. Real incident: a company put API keys in their Dockerfile, pushed the image as public to Docker Hub, and received a $50,000 AWS bill from crypto mining.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Never Do This
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# ❌ EXTREMELY DANGEROUS — visible in docker history forever!&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; DATABASE_PASSWORD=mysupersecretpassword123&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; JWT_SECRET=my-jwt-secret-key&lt;/span&gt;

&lt;span class="c"&gt;# ❌ ALSO DANGEROUS — even in RUN, it stays in the layer!&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;sk-prod-abc123 &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; ./configure.sh

&lt;span class="c"&gt;# ❌ BAD — copying .env file into image&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; .env /app/.env  # .env is now inside the image layers!&lt;/span&gt;

&lt;span class="c"&gt;# Check what's visible (run this on your image)&lt;/span&gt;
docker history --no-trunc myapp | grep -i "password\|secret\|key\|token"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Approach 1: Runtime Environment Variables
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Pass at runtime — not baked into the image&lt;/span&gt;
docker run &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;DATABASE_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;secret myapp

&lt;span class="c"&gt;# From a file — more manageable (file security is your responsibility)&lt;/span&gt;
docker run &lt;span class="nt"&gt;--env-file&lt;/span&gt; secrets.env myapp

&lt;span class="c"&gt;# secrets.env format (add this file to .gitignore!)&lt;/span&gt;
&lt;span class="c"&gt;# DATABASE_URL=postgresql://user:pass@host:5432/db&lt;/span&gt;
&lt;span class="c"&gt;# API_KEY=sk-prod-abc123&lt;/span&gt;
&lt;span class="c"&gt;# JWT_SECRET=super-secret-jwt-key&lt;/span&gt;

&lt;span class="c"&gt;# CAVEAT: Still visible in 'docker inspect'!&lt;/span&gt;
docker inspect myapp | jq &lt;span class="s1"&gt;'.[0].Config.Env'&lt;/span&gt;
&lt;span class="c"&gt;# [&lt;/span&gt;
&lt;span class="c"&gt;#   "DATABASE_PASSWORD=secret",  ← Visible to anyone with docker inspect!&lt;/span&gt;
&lt;span class="c"&gt;#   "PATH=/usr/local/sbin:..."&lt;/span&gt;
&lt;span class="c"&gt;# ]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Approach 2: Docker Secrets (Swarm Mode)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create a Docker secret (stored encrypted in Swarm Raft)&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"mysupersecretpassword"&lt;/span&gt; | docker secret create db_password -
&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;"sk-prod-abc123secret"&lt;/span&gt; | docker secret create api_key -

&lt;span class="c"&gt;# List secrets (values NOT shown)&lt;/span&gt;
docker secret &lt;span class="nb"&gt;ls&lt;/span&gt;

&lt;span class="c"&gt;# Use in service (mounted as file at /run/secrets/secretname)&lt;/span&gt;
docker service create &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--secret&lt;/span&gt; db_password &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--secret&lt;/span&gt; api_key &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--env&lt;/span&gt; &lt;span class="nv"&gt;DB_PASSWORD_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/run/secrets/db_password &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--env&lt;/span&gt; &lt;span class="nv"&gt;API_KEY_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/run/secrets/api_key &lt;span class="se"&gt;\&lt;/span&gt;
  myapp

&lt;span class="c"&gt;# Inside container, secrets are files:&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; /run/secrets/db_password  &lt;span class="c"&gt;# mysupersecretpassword&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; /run/secrets/api_key      &lt;span class="c"&gt;# sk-prod-abc123secret&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# docker-compose.yml with secrets (Swarm mode)&lt;/span&gt;
&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3.8"&lt;/span&gt;

&lt;span class="na"&gt;secrets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;db_password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;external&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;    &lt;span class="c1"&gt;# Secret already created via docker secret create&lt;/span&gt;
  &lt;span class="na"&gt;api_key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;external&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;api&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myapp:latest&lt;/span&gt;
    &lt;span class="na"&gt;secrets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;db_password&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;api_key&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# Point to secret files, not actual values&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;DB_PASSWORD_FILE=/run/secrets/db_password&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;API_KEY_FILE=/run/secrets/api_key&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Approach 3: AWS Secrets Manager (Production Best Practice)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/sh&lt;/span&gt;
&lt;span class="c"&gt;# entrypoint.sh — Fetch secrets at startup from AWS Secrets Manager&lt;/span&gt;
&lt;span class="c"&gt;# Container uses an IAM role (no hardcoded AWS credentials needed!)&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Fetching secrets from AWS Secrets Manager..."&lt;/span&gt;

&lt;span class="c"&gt;# Fetch database password&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;DB_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;aws secretsmanager get-secret-value &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--secret-id&lt;/span&gt; prod/myapp/db_password &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; SecretString &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; text &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; ap-south-1&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Fetch API key&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;aws secretsmanager get-secret-value &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--secret-id&lt;/span&gt; prod/myapp/api_key &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; SecretString &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; text &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; ap-south-1&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Secrets loaded successfully"&lt;/span&gt;

&lt;span class="c"&gt;# Execute the main command (e.g., node server.js)&lt;/span&gt;
&lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Approach 4: BuildKit Secret Mounts (Build-time Secrets)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# syntax=docker/dockerfile:1&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:18-alpine&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="c"&gt;# Secret mounted ONLY during this RUN — not stored in the image layer!&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nt"&gt;--mount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;secret,id&lt;span class="o"&gt;=&lt;/span&gt;npmrc,target&lt;span class="o"&gt;=&lt;/span&gt;/root/.npmrc &lt;span class="se"&gt;\
&lt;/span&gt;    npm &lt;span class="nb"&gt;install&lt;/span&gt;  &lt;span class="c"&gt;# Can access private npm registry using .npmrc secret&lt;/span&gt;

&lt;span class="c"&gt;# Private repo clone during build&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nt"&gt;--mount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ssh &lt;span class="se"&gt;\
&lt;/span&gt;    git clone git@github.com:myorg/private-repo.git /app/lib
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Build with secret (secret never enters the image!)&lt;/span&gt;
docker build &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--secret&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;npmrc,src&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;/.npmrc &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-t&lt;/span&gt; myapp &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="c"&gt;# SSH forwarding for private repos&lt;/span&gt;
&lt;span class="nb"&gt;eval&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;ssh-agent&lt;span class="si"&gt;)&lt;/span&gt;
ssh-add ~/.ssh/id_rsa
docker build &lt;span class="nt"&gt;--ssh&lt;/span&gt; default &lt;span class="nt"&gt;-t&lt;/span&gt; myapp &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Always add &lt;code&gt;.env&lt;/code&gt; to both &lt;code&gt;.gitignore&lt;/code&gt; and &lt;code&gt;.dockerignore&lt;/code&gt;. Commit a &lt;code&gt;.env.example&lt;/code&gt; template without values so team members know which variables are required. In production, never use &lt;code&gt;.env&lt;/code&gt; files — use a proper secrets management solution.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Secrets manager options include: AWS Secrets Manager (paid, excellent rotation support), HashiCorp Vault (free, powerful, complex), Kubernetes Secrets (base64-encoded — not truly secret!), Doppler (developer-friendly SaaS), Azure Key Vault, and GCP Secret Manager. Choose based on your cloud provider.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  BuildKit — Modern Docker Build Engine
&lt;/h2&gt;

&lt;p&gt;BuildKit is the default build engine in Docker 23+. It is &lt;strong&gt;much faster, safer, and more feature-rich&lt;/strong&gt; than the older builder. Parallel builds, better caching, secret mounts — all of these come from BuildKit.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Old builder: sequential layers, no cache import/export, no secret mounts. BuildKit: parallel stage builds, registry cache, secret mounts, SSH forwarding, multi-arch support. Master BuildKit once and CI/CD build times improve dramatically.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Enabling BuildKit
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check if BuildKit is available&lt;/span&gt;
docker buildx version
&lt;span class="c"&gt;# github.com/docker/buildx v0.12.0 docker-desktop&lt;/span&gt;

&lt;span class="c"&gt;# Docker 23+ enables it automatically&lt;/span&gt;
&lt;span class="c"&gt;# For older versions:&lt;/span&gt;

&lt;span class="c"&gt;# Option 1: Per-build environment variable&lt;/span&gt;
&lt;span class="nv"&gt;DOCKER_BUILDKIT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 docker build &lt;span class="nt"&gt;-t&lt;/span&gt; myapp &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="c"&gt;# Option 2: Enable permanently in daemon.json&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;nano /etc/docker/daemon.json
&lt;span class="c"&gt;# Add: { "features": { "buildkit": true } }&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl restart docker

&lt;span class="c"&gt;# Option 3: Set DOCKER_BUILDKIT in shell profile&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'export DOCKER_BUILDKIT=1'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; ~/.bashrc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  BuildKit Special Features — syntax=docker/dockerfile:1
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# This comment enables the latest BuildKit Dockerfile syntax&lt;/span&gt;
&lt;span class="c"&gt;# syntax=docker/dockerfile:1&lt;/span&gt;

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:18-alpine&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="c"&gt;# Feature 1: Secret mounts — never stored in image layers!&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nt"&gt;--mount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;secret,id&lt;span class="o"&gt;=&lt;/span&gt;npmrc,target&lt;span class="o"&gt;=&lt;/span&gt;/root/.npmrc &lt;span class="se"&gt;\
&lt;/span&gt;    npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--legacy-peer-deps&lt;/span&gt;

&lt;span class="c"&gt;# Feature 2: Cache mounts — persist between builds!&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nt"&gt;--mount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;cache,target&lt;span class="o"&gt;=&lt;/span&gt;/root/.npm &lt;span class="se"&gt;\
&lt;/span&gt;    npm ci  &lt;span class="c"&gt;# npm cache persists across builds = MUCH faster!&lt;/span&gt;

&lt;span class="c"&gt;# Feature 3: Bind mounts — mount host directory without COPY&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nt"&gt;--mount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;bind&lt;/span&gt;,source&lt;span class="o"&gt;=&lt;/span&gt;package.json,target&lt;span class="o"&gt;=&lt;/span&gt;package.json &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nt"&gt;--mount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;bind&lt;/span&gt;,source&lt;span class="o"&gt;=&lt;/span&gt;package-lock.json,target&lt;span class="o"&gt;=&lt;/span&gt;package-lock.json &lt;span class="se"&gt;\
&lt;/span&gt;    npm ci

&lt;span class="c"&gt;# Feature 4: SSH forwarding&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nt"&gt;--mount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ssh &lt;span class="se"&gt;\
&lt;/span&gt;    git clone git@github.com:private/repo.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  docker buildx Commands
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# List all builders&lt;/span&gt;
docker buildx &lt;span class="nb"&gt;ls&lt;/span&gt;

&lt;span class="c"&gt;# Create a new builder with docker-container driver (more features)&lt;/span&gt;
docker buildx create &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; mybuilder &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--driver&lt;/span&gt; docker-container &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--bootstrap&lt;/span&gt;
docker buildx use mybuilder

&lt;span class="c"&gt;# Inspect builder (shows supported platforms)&lt;/span&gt;
docker buildx inspect mybuilder

&lt;span class="c"&gt;# Build for current platform (like regular docker build)&lt;/span&gt;
docker buildx build &lt;span class="nt"&gt;-t&lt;/span&gt; myapp &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="c"&gt;# Build for a specific platform&lt;/span&gt;
docker buildx build &lt;span class="nt"&gt;--platform&lt;/span&gt; linux/amd64 &lt;span class="nt"&gt;-t&lt;/span&gt; myapp:amd64 &lt;span class="nt"&gt;--load&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
docker buildx build &lt;span class="nt"&gt;--platform&lt;/span&gt; linux/arm64 &lt;span class="nt"&gt;-t&lt;/span&gt; myapp:arm64 &lt;span class="nt"&gt;--load&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="c"&gt;# Build and push multi-arch image in one command!&lt;/span&gt;
docker buildx build &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--platform&lt;/span&gt; linux/amd64,linux/arm64 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-t&lt;/span&gt; username/myapp:latest &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--push&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="c"&gt;# Remove builder&lt;/span&gt;
docker buildx &lt;span class="nb"&gt;rm &lt;/span&gt;mybuilder
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Registry Cache — CI/CD Game Changer
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Export cache to registry (persists between CI runs)&lt;/span&gt;
docker buildx build &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--cache-to&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;registry,ref&lt;span class="o"&gt;=&lt;/span&gt;myapp:buildcache,mode&lt;span class="o"&gt;=&lt;/span&gt;max &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--cache-from&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;registry,ref&lt;span class="o"&gt;=&lt;/span&gt;myapp:buildcache &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-t&lt;/span&gt; myapp:latest &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--push&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="c"&gt;# mode=max: Cache all intermediate layers (maximum cache hits)&lt;/span&gt;
&lt;span class="c"&gt;# mode=min: Cache only final layer (smaller cache size)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# GitHub Actions — Optimal BuildKit caching&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set up Docker Buildx&lt;/span&gt;
  &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/setup-buildx-action@v3&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build and push with cache&lt;/span&gt;
  &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/build-push-action@v5&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
    &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myapp:latest&lt;/span&gt;
    &lt;span class="na"&gt;cache-from&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;type=gha&lt;/span&gt;          &lt;span class="c1"&gt;# GitHub Actions cache (free!)&lt;/span&gt;
    &lt;span class="na"&gt;cache-to&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;type=gha,mode=max&lt;/span&gt;   &lt;span class="c1"&gt;# Save cache for next run&lt;/span&gt;
    &lt;span class="c1"&gt;# Result: 5-minute builds → 45-second builds on cache hit!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Without caching: a Node app build takes 4–5 minutes. With caching (dependencies unchanged): 30–60 seconds. For large enterprise apps: 20 minutes → 2 minutes. This difference directly affects production deployment frequency.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; With multi-arch builds, &lt;code&gt;--load&lt;/code&gt; (local image) and &lt;code&gt;--push&lt;/code&gt; (registry) cannot be used simultaneously in most cases. For single-arch builds use &lt;code&gt;--load&lt;/code&gt;; for multi-arch, push directly to the registry with &lt;code&gt;--push&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Multi-Architecture Images — Supporting ARM and x86
&lt;/h2&gt;

&lt;p&gt;You develop on a MacBook M1/M2/M3 (ARM64) and deploy to AWS EC2 (amd64). Multi-arch images work on both platforms without issues — no need for separate Dockerfiles.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Apple Silicon Macs (ARM64) have become standard dev machines. AWS Graviton instances (ARM64) offer the same performance at 40% lower cost than x86. If your image only supports amd64, ARM machines will produce a "wrong platform" error. Build multi-arch images to support everyone.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Setup and Build
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Step 1: Install QEMU (enables emulation of other architectures)&lt;/span&gt;
docker run &lt;span class="nt"&gt;--privileged&lt;/span&gt; &lt;span class="nt"&gt;--rm&lt;/span&gt; tonistiigi/binfmt &lt;span class="nt"&gt;--install&lt;/span&gt; all
&lt;span class="c"&gt;# Installs QEMU user-space emulators for ARM, etc.&lt;/span&gt;

&lt;span class="c"&gt;# Verify QEMU installation&lt;/span&gt;
&lt;span class="nb"&gt;ls&lt;/span&gt; /proc/sys/fs/binfmt_misc/
&lt;span class="c"&gt;# Should show: qemu-aarch64, qemu-arm, etc.&lt;/span&gt;

&lt;span class="c"&gt;# Step 2: Create a multi-arch capable builder&lt;/span&gt;
docker buildx create &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; multiarch-builder &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--driver&lt;/span&gt; docker-container &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--bootstrap&lt;/span&gt;
docker buildx use multiarch-builder

&lt;span class="c"&gt;# Step 3: Inspect — verify which platforms are supported&lt;/span&gt;
docker buildx inspect multiarch-builder &lt;span class="nt"&gt;--bootstrap&lt;/span&gt;
&lt;span class="c"&gt;# Platforms: linux/amd64, linux/arm64, linux/arm/v7, linux/386...&lt;/span&gt;

&lt;span class="c"&gt;# Step 4: Build and push multi-arch image in one command&lt;/span&gt;
docker buildx build &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--platform&lt;/span&gt; linux/amd64,linux/arm64 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--tag&lt;/span&gt; username/myapp:latest &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--tag&lt;/span&gt; username/myapp:v1.0.0 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--push&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="c"&gt;# Step 5: Verify multi-arch manifest&lt;/span&gt;
docker manifest inspect username/myapp:latest
&lt;span class="c"&gt;# Shows separate manifests for each architecture&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Platform-Specific Handling in Dockerfile
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# syntax=docker/dockerfile:1&lt;/span&gt;

&lt;span class="c"&gt;# BuildKit injects these automatically&lt;/span&gt;
&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; TARGETPLATFORM&lt;/span&gt;
&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; TARGETARCH&lt;/span&gt;
&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; TARGETOS&lt;/span&gt;

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:18-alpine&lt;/span&gt;

&lt;span class="c"&gt;# Platform-specific commands (if needed)&lt;/span&gt;
&lt;span class="k"&gt;RUN case&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$TARGETARCH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    amd64&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Building for x86_64"&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    arm64&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Building for ARM64"&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    arm&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Building for ARM v7"&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="k"&gt;esac&lt;/span&gt;

&lt;span class="c"&gt;# Most Node.js apps need no platform-specific code&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm ci &lt;span class="nt"&gt;--only&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;production
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;

&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; node&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 3000&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["node", "server.js"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  GitHub Actions — Multi-Arch CI/CD
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Multi-Arch Build&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set up QEMU (ARM emulation)&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/setup-qemu-action@v3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;platforms&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;linux/amd64,linux/arm64&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set up Docker Buildx&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/setup-buildx-action@v3&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Login to Docker Hub&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/login-action@v3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.DOCKER_USERNAME }}&lt;/span&gt;
          &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.DOCKER_TOKEN }}&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build and push multi-arch&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/build-push-action@v5&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
          &lt;span class="na"&gt;platforms&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;linux/amd64,linux/arm64&lt;/span&gt;
          &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
          &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;username/myapp:latest&lt;/span&gt;
          &lt;span class="na"&gt;cache-from&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;type=gha&lt;/span&gt;
          &lt;span class="na"&gt;cache-to&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;type=gha,mode=max&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; QEMU emulation makes ARM64 builds 5–10x slower on an amd64 machine. Solutions: use native ARM builders (GitHub has paid ARM64 runners), or run matrix builds (amd64 and arm64 in parallel). QEMU is fine for small images.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; AWS Graviton3 (arm64) instances are 20–40% cheaper than equivalent x86 instances for the same workload. Build one multi-arch image, deploy on Graviton — significant cost savings at scale. t4g instances are cheaper than t3, c7g instances are cheaper than c6i.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Docker Context — Managing Multiple Docker Hosts
&lt;/h2&gt;

&lt;p&gt;Docker contexts let you manage multiple Docker hosts from one machine — local, staging server, production server — all with the same &lt;code&gt;docker&lt;/code&gt; command. Switch context and all commands go there.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Contexts are useful when you want to run Docker commands on remote servers without SSH-ing in manually, or when managing multiple environments (dev, staging, production) from a CI/CD workflow.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Context Management Commands
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# List all contexts (* = currently active)&lt;/span&gt;
docker context &lt;span class="nb"&gt;ls&lt;/span&gt;
&lt;span class="c"&gt;# NAME           DESCRIPTION   DOCKER ENDPOINT               KUBERNETES ENDPOINT&lt;/span&gt;
&lt;span class="c"&gt;# default *      ...           unix:///var/run/docker.sock&lt;/span&gt;
&lt;span class="c"&gt;# remote-server  ...           ssh://deploy@203.0.113.10&lt;/span&gt;

&lt;span class="c"&gt;# Create context for a remote server (uses SSH)&lt;/span&gt;
docker context create remote-server &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--description&lt;/span&gt; &lt;span class="s2"&gt;"Production server"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--docker&lt;/span&gt; &lt;span class="s2"&gt;"host=ssh://deploy@203.0.113.10"&lt;/span&gt;

&lt;span class="c"&gt;# Create context with a specific SSH key&lt;/span&gt;
docker context create staging &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--docker&lt;/span&gt; &lt;span class="s2"&gt;"host=ssh://ubuntu@staging.myapp.com"&lt;/span&gt;

&lt;span class="c"&gt;# Switch to remote context&lt;/span&gt;
docker context use remote-server
&lt;span class="c"&gt;# All docker commands now go to the remote host!&lt;/span&gt;

docker ps      &lt;span class="c"&gt;# Shows containers on remote server!&lt;/span&gt;
docker images  &lt;span class="c"&gt;# Shows images on remote server!&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; nginx  &lt;span class="c"&gt;# Runs on remote server!&lt;/span&gt;

&lt;span class="c"&gt;# Switch back to local&lt;/span&gt;
docker context use default

&lt;span class="c"&gt;# One-off command in a specific context (without switching)&lt;/span&gt;
docker &lt;span class="nt"&gt;--context&lt;/span&gt; remote-server ps
docker &lt;span class="nt"&gt;--context&lt;/span&gt; remote-server logs myapp &lt;span class="nt"&gt;-f&lt;/span&gt;

&lt;span class="c"&gt;# Inspect a context&lt;/span&gt;
docker context inspect remote-server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Export/Import Contexts
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Export context (share with team)&lt;/span&gt;
docker context &lt;span class="nb"&gt;export &lt;/span&gt;remote-server &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; production.dockercontext

&lt;span class="c"&gt;# Import on another machine&lt;/span&gt;
docker context import production-server production.dockercontext

&lt;span class="c"&gt;# Remove a context&lt;/span&gt;
docker context &lt;span class="nb"&gt;rm &lt;/span&gt;old-server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Context with AWS ECS
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create ECS context (deploy directly to ECS!)&lt;/span&gt;
docker context create ecs myecscontext
&lt;span class="c"&gt;# Prompts for AWS credentials/profile&lt;/span&gt;

&lt;span class="c"&gt;# Switch to ECS context&lt;/span&gt;
docker context use myecscontext

&lt;span class="c"&gt;# Now docker compose commands deploy to ECS!&lt;/span&gt;
docker compose up  &lt;span class="c"&gt;# Deploys to ECS Fargate!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Define hosts in &lt;code&gt;~/.ssh/config&lt;/code&gt;, then reference them in a context: &lt;code&gt;docker context create prod --docker "host=ssh://prod-server"&lt;/code&gt;. SSH config can include jump hosts and identity files — complex infrastructure can be managed cleanly this way.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; A common mistake is switching to a remote context, doing some work, and forgetting about it. The next local command runs on the remote host instead. Always check the current context with &lt;code&gt;docker context ls&lt;/code&gt;. Consider showing the current context in your shell prompt using Starship or oh-my-zsh plugins.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Docker Plugins — Extending Functionality
&lt;/h2&gt;

&lt;p&gt;Docker plugins extend Docker daemon capabilities with custom volume drivers, network drivers, and authorization plugins. They are useful in enterprise environments and for integrating cloud storage.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Plugin types — Volume plugins: connect cloud storage (AWS EBS, NFS, GlusterFS) to Docker volumes. Network plugins: custom networking (Weave, Calico, Flannel). Authorization plugins: centralized access control defining what each container is allowed to do.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Volume Plugins
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# List installed plugins&lt;/span&gt;
docker plugin &lt;span class="nb"&gt;ls&lt;/span&gt;

&lt;span class="c"&gt;# Install NFS volume plugin (mount NFS shares as Docker volumes)&lt;/span&gt;
docker plugin &lt;span class="nb"&gt;install &lt;/span&gt;vieux/sshfs &lt;span class="nv"&gt;DEBUG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1

&lt;span class="c"&gt;# Install local-persist plugin (volumes survive container removal)&lt;/span&gt;
docker plugin &lt;span class="nb"&gt;install &lt;/span&gt;cwspear/docker-local-persist-volume-plugin

&lt;span class="c"&gt;# Enable/disable plugin&lt;/span&gt;
docker plugin &lt;span class="nb"&gt;enable &lt;/span&gt;pluginname
docker plugin disable pluginname

&lt;span class="c"&gt;# Remove plugin&lt;/span&gt;
docker plugin &lt;span class="nb"&gt;rm &lt;/span&gt;pluginname

&lt;span class="c"&gt;# Inspect plugin&lt;/span&gt;
docker plugin inspect vieux/sshfs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# SSHFS Plugin — Mount a remote directory as a Docker volume&lt;/span&gt;
&lt;span class="c"&gt;# Useful for: shared config files, remote storage&lt;/span&gt;

&lt;span class="c"&gt;# First install the plugin&lt;/span&gt;
docker plugin &lt;span class="nb"&gt;install &lt;/span&gt;vieux/sshfs

&lt;span class="c"&gt;# Create volume using plugin&lt;/span&gt;
docker volume create &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--driver&lt;/span&gt; vieux/sshfs &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--opt&lt;/span&gt; &lt;span class="nv"&gt;sshcmd&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;user@remote-host:/path/to/dir &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--opt&lt;/span&gt; &lt;span class="nv"&gt;password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;mysshpassword &lt;span class="se"&gt;\&lt;/span&gt;
  remote-data

&lt;span class="c"&gt;# Use volume in container&lt;/span&gt;
docker run &lt;span class="nt"&gt;-v&lt;/span&gt; remote-data:/app/data myapp

&lt;span class="c"&gt;# AWS EBS volume plugin (for persistent EBS volumes)&lt;/span&gt;
docker plugin &lt;span class="nb"&gt;install &lt;/span&gt;rexray/ebs &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nv"&gt;EBS_REGION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ap-south-1

docker volume create &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--driver&lt;/span&gt; rexray/ebs &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--opt&lt;/span&gt; &lt;span class="nv"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;20 &lt;span class="se"&gt;\&lt;/span&gt;
  my-ebs-volume
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Authorization Plugins — Enterprise Security
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# OPA (Open Policy Agent) authorization plugin&lt;/span&gt;
&lt;span class="c"&gt;# Define policies: what each container is allowed to do&lt;/span&gt;

&lt;span class="c"&gt;# Install&lt;/span&gt;
docker plugin &lt;span class="nb"&gt;install &lt;/span&gt;openpolicyagent/opa-docker-authz-local:v0.2 &lt;span class="se"&gt;\&lt;/span&gt;
  policy-file&lt;span class="o"&gt;=&lt;/span&gt;/etc/docker/policies/authz.rego

&lt;span class="c"&gt;# authz.rego policy example (Rego language)&lt;/span&gt;
&lt;span class="c"&gt;# package docker.authz&lt;/span&gt;
&lt;span class="c"&gt;# allow {&lt;/span&gt;
&lt;span class="c"&gt;#   not deny&lt;/span&gt;
&lt;span class="c"&gt;# }&lt;/span&gt;
&lt;span class="c"&gt;# deny {&lt;/span&gt;
&lt;span class="c"&gt;#   input.Body.HostConfig.Privileged == true  # Block privileged containers&lt;/span&gt;
&lt;span class="c"&gt;# }&lt;/span&gt;

&lt;span class="c"&gt;# Enable in daemon.json:&lt;/span&gt;
&lt;span class="c"&gt;# {&lt;/span&gt;
&lt;span class="c"&gt;#   "authorization-plugins": ["openpolicyagent/opa-docker-authz-local:v0.2"]&lt;/span&gt;
&lt;span class="c"&gt;# }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; Docker plugins run with daemon-level access. Only use trusted, well-maintained plugins. Review permissions carefully when installing — a malicious plugin can compromise the entire Docker daemon.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Many plugin use cases are now better handled by Kubernetes and CSI (Container Storage Interface) drivers. If you are using Kubernetes, explore CSI drivers instead of Docker plugins — the ecosystem is better and offers more options.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Resource Management — Limiting Container Resources
&lt;/h2&gt;

&lt;p&gt;Without resource limits, a misbehaving container can consume all the memory and CPU on the host, crashing everything else. Apply limits to every container in production.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; &lt;code&gt;:(){ :|:&amp;amp; };:&lt;/code&gt; is a bash fork bomb. Without &lt;code&gt;--pids-limit&lt;/code&gt;, this can crash an entire machine from a single container. In the real world, buggy code leaks memory or spawns infinite processes. Always set limits.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Memory Limits
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Hard memory limit — OOM killer triggers if exceeded&lt;/span&gt;
docker run &lt;span class="nt"&gt;--memory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;512m myapp

&lt;span class="c"&gt;# Memory + swap limit&lt;/span&gt;
docker run &lt;span class="nt"&gt;--memory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;512m &lt;span class="nt"&gt;--memory-swap&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1g myapp
&lt;span class="c"&gt;# RAM: 512MB, Swap: 512MB (total 1GB, swap = total - ram)&lt;/span&gt;

&lt;span class="c"&gt;# Disable swap for container&lt;/span&gt;
docker run &lt;span class="nt"&gt;--memory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;512m &lt;span class="nt"&gt;--memory-swap&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;512m myapp

&lt;span class="c"&gt;# Soft limit — suggestion, not enforced (for low-memory situations)&lt;/span&gt;
docker run &lt;span class="nt"&gt;--memory-reservation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;256m myapp

&lt;span class="c"&gt;# OOM kill disable (risky — lets container grow without being killed)&lt;/span&gt;
docker run &lt;span class="nt"&gt;--oom-kill-disable&lt;/span&gt; myapp  &lt;span class="c"&gt;# Use with extreme caution!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  CPU Limits
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Hard CPU limit — cannot exceed X cores&lt;/span&gt;
docker run &lt;span class="nt"&gt;--cpus&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1.5 myapp       &lt;span class="c"&gt;# Max 1.5 CPU cores&lt;/span&gt;
docker run &lt;span class="nt"&gt;--cpus&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0.25 myapp      &lt;span class="c"&gt;# Max 25% of one CPU core&lt;/span&gt;

&lt;span class="c"&gt;# CPU shares — relative weight (soft limit, when contention exists)&lt;/span&gt;
docker run &lt;span class="nt"&gt;--cpu-shares&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;512 myapp  &lt;span class="c"&gt;# Default is 1024; gets half the CPU when contested&lt;/span&gt;
docker run &lt;span class="nt"&gt;--cpu-shares&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2048 myapp &lt;span class="c"&gt;# Gets double the default CPU priority&lt;/span&gt;

&lt;span class="c"&gt;# Pin to specific CPU cores (NUMA optimization, isolation)&lt;/span&gt;
docker run &lt;span class="nt"&gt;--cpuset-cpus&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0,1 myapp  &lt;span class="c"&gt;# Only use CPUs 0 and 1&lt;/span&gt;
docker run &lt;span class="nt"&gt;--cpuset-cpus&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2-5 myapp  &lt;span class="c"&gt;# Use CPUs 2, 3, 4, 5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  I/O and Other Limits
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Disk I/O limits&lt;/span&gt;
docker run &lt;span class="nt"&gt;--device-read-bps&lt;/span&gt; /dev/sda:100mb myapp    &lt;span class="c"&gt;# Max 100MB/s read&lt;/span&gt;
docker run &lt;span class="nt"&gt;--device-write-bps&lt;/span&gt; /dev/sda:50mb myapp    &lt;span class="c"&gt;# Max 50MB/s write&lt;/span&gt;
docker run &lt;span class="nt"&gt;--device-read-iops&lt;/span&gt; /dev/sda:1000 myapp    &lt;span class="c"&gt;# Max 1000 IOPS read&lt;/span&gt;

&lt;span class="c"&gt;# PID limit (prevent fork bombs!)&lt;/span&gt;
docker run &lt;span class="nt"&gt;--pids-limit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;100 myapp  &lt;span class="c"&gt;# Max 100 processes/threads&lt;/span&gt;

&lt;span class="c"&gt;# Open file descriptor limit&lt;/span&gt;
docker run &lt;span class="nt"&gt;--ulimit&lt;/span&gt; &lt;span class="nv"&gt;nofile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1024:1024 myapp
&lt;span class="c"&gt;# Format: soft_limit:hard_limit&lt;/span&gt;

&lt;span class="c"&gt;# Update limits on a RUNNING container (no restart needed!)&lt;/span&gt;
docker update &lt;span class="nt"&gt;--memory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1g &lt;span class="nt"&gt;--cpus&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2 myapp

&lt;span class="c"&gt;# Check resource usage (real-time)&lt;/span&gt;
docker stats                     &lt;span class="c"&gt;# All containers&lt;/span&gt;
docker stats myapp               &lt;span class="c"&gt;# Specific container&lt;/span&gt;
docker stats &lt;span class="nt"&gt;--no-stream&lt;/span&gt; myapp   &lt;span class="c"&gt;# Snapshot, not live&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Production Resource Sizing Guide
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Service Type&lt;/th&gt;
&lt;th&gt;CPU&lt;/th&gt;
&lt;th&gt;Memory&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Web API (Node.js)&lt;/td&gt;
&lt;td&gt;0.5–1.0&lt;/td&gt;
&lt;td&gt;256–512MB&lt;/td&gt;
&lt;td&gt;Scale horizontally&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Database (PostgreSQL)&lt;/td&gt;
&lt;td&gt;2.0+&lt;/td&gt;
&lt;td&gt;1–4GB&lt;/td&gt;
&lt;td&gt;Depends on dataset size&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cache (Redis)&lt;/td&gt;
&lt;td&gt;0.25&lt;/td&gt;
&lt;td&gt;256–512MB&lt;/td&gt;
&lt;td&gt;Set maxmemory in Redis config&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Background Worker&lt;/td&gt;
&lt;td&gt;1.0&lt;/td&gt;
&lt;td&gt;512MB–1GB&lt;/td&gt;
&lt;td&gt;Depends on task type&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Message Queue&lt;/td&gt;
&lt;td&gt;0.5&lt;/td&gt;
&lt;td&gt;512MB&lt;/td&gt;
&lt;td&gt;RabbitMQ/Kafka varies&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Monitoring Agent&lt;/td&gt;
&lt;td&gt;0.1&lt;/td&gt;
&lt;td&gt;64–128MB&lt;/td&gt;
&lt;td&gt;Should be minimal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nginx Proxy&lt;/td&gt;
&lt;td&gt;0.25&lt;/td&gt;
&lt;td&gt;128MB&lt;/td&gt;
&lt;td&gt;Very lightweight&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# docker-compose.yml with resource limits&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;api&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myapi:latest&lt;/span&gt;
    &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;limits&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;cpus&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0.5"&lt;/span&gt;
          &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;512M&lt;/span&gt;
        &lt;span class="na"&gt;reservations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;cpus&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0.25"&lt;/span&gt;
          &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;256M&lt;/span&gt;

  &lt;span class="na"&gt;db&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres:15-alpine&lt;/span&gt;
    &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;limits&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;cpus&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2.0"&lt;/span&gt;
          &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;2G&lt;/span&gt;
        &lt;span class="na"&gt;reservations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;cpus&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1.0"&lt;/span&gt;
          &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1G&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Strategy for setting limits: first run without limits and observe actual usage with &lt;code&gt;docker stats&lt;/code&gt;. Then set limit = actual_peak × 1.5 (with buffer). Test on staging to verify the app works correctly within limits. Deploy to production and track OOM kills and CPU throttling via monitoring.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Debugging Containers — When Nothing Works
&lt;/h2&gt;

&lt;p&gt;"It works on my machine" — everyone has heard this. Containers have their own issues. Here is a systematic debugging methodology for quickly resolving production problems.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Debugging order: Status → Logs → Resources → Exec into container → Processes → Network → Events. Follow this order — 80% of issues are resolved via logs or exec. Advanced tools are only needed when the basics fail.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Systematic Debugging Playbook
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# ===== STEP 1: Container Status =====&lt;/span&gt;
docker ps &lt;span class="nt"&gt;-a&lt;/span&gt;  &lt;span class="c"&gt;# -a shows stopped containers too&lt;/span&gt;
&lt;span class="c"&gt;# STATUS: Up 2 hours (good) | Exited (1) 5 min ago (bad) | Restarting (crash loop!)&lt;/span&gt;

&lt;span class="c"&gt;# Detailed status&lt;/span&gt;
docker inspect myapp | jq &lt;span class="s1"&gt;'.[0].State'&lt;/span&gt;
&lt;span class="c"&gt;# {&lt;/span&gt;
&lt;span class="c"&gt;#   "Status": "exited",&lt;/span&gt;
&lt;span class="c"&gt;#   "Running": false,&lt;/span&gt;
&lt;span class="c"&gt;#   "ExitCode": 137,  ← 137 = OOM kill! 1 = app error, 0 = clean exit&lt;/span&gt;
&lt;span class="c"&gt;#   "OOMKilled": true,&lt;/span&gt;
&lt;span class="c"&gt;#   "Error": ""&lt;/span&gt;
&lt;span class="c"&gt;# }&lt;/span&gt;

&lt;span class="c"&gt;# ===== STEP 2: Logs =====&lt;/span&gt;
docker logs myapp              &lt;span class="c"&gt;# All logs&lt;/span&gt;
docker logs myapp &lt;span class="nt"&gt;--tail&lt;/span&gt; 100   &lt;span class="c"&gt;# Last 100 lines&lt;/span&gt;
docker logs myapp &lt;span class="nt"&gt;-f&lt;/span&gt;           &lt;span class="c"&gt;# Follow (like tail -f)&lt;/span&gt;
docker logs myapp &lt;span class="nt"&gt;--since&lt;/span&gt; 30m  &lt;span class="c"&gt;# Last 30 minutes&lt;/span&gt;
docker logs myapp 2&amp;gt;&amp;amp;1 | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; error  &lt;span class="c"&gt;# Filter errors&lt;/span&gt;

&lt;span class="c"&gt;# ===== STEP 3: Resource Usage =====&lt;/span&gt;
docker stats myapp &lt;span class="nt"&gt;--no-stream&lt;/span&gt;
&lt;span class="c"&gt;# CONTAINER  CPU %  MEM USAGE / LIMIT   MEM %   NET I/O    BLOCK I/O&lt;/span&gt;
&lt;span class="c"&gt;# myapp      95.2%  490MiB / 512MiB    95.7%   1.2GB/500MB  0B/0B&lt;/span&gt;
&lt;span class="c"&gt;# ← 95% memory → OOM kill is imminent!&lt;/span&gt;

&lt;span class="c"&gt;# ===== STEP 4: Shell into Running Container =====&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; myapp bash    &lt;span class="c"&gt;# bash shell (Debian/Ubuntu)&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; myapp sh      &lt;span class="c"&gt;# sh shell (Alpine)&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; myapp /bin/sh &lt;span class="c"&gt;# explicit path&lt;/span&gt;

&lt;span class="c"&gt;# Run a specific command without interactive shell&lt;/span&gt;
docker &lt;span class="nb"&gt;exec &lt;/span&gt;myapp &lt;span class="nb"&gt;env&lt;/span&gt;          &lt;span class="c"&gt;# Check environment variables&lt;/span&gt;
docker &lt;span class="nb"&gt;exec &lt;/span&gt;myapp &lt;span class="nb"&gt;cat&lt;/span&gt; /app/config.json

&lt;span class="c"&gt;# ===== STEP 5: Check Processes =====&lt;/span&gt;
docker top myapp               &lt;span class="c"&gt;# Processes running in container&lt;/span&gt;
docker &lt;span class="nb"&gt;exec &lt;/span&gt;myapp ps aux       &lt;span class="c"&gt;# More detailed process list&lt;/span&gt;

&lt;span class="c"&gt;# ===== STEP 6: Network Debugging =====&lt;/span&gt;
docker inspect myapp | jq &lt;span class="s1"&gt;'.[0].NetworkSettings.Networks'&lt;/span&gt;
docker &lt;span class="nb"&gt;exec &lt;/span&gt;myapp netstat &lt;span class="nt"&gt;-tulpn&lt;/span&gt;    &lt;span class="c"&gt;# Listening ports&lt;/span&gt;
docker &lt;span class="nb"&gt;exec &lt;/span&gt;myapp curl &lt;span class="nt"&gt;-v&lt;/span&gt; http://othercontainer:8080/health

&lt;span class="c"&gt;# Test DNS resolution in container&lt;/span&gt;
docker &lt;span class="nb"&gt;exec &lt;/span&gt;myapp nslookup db        &lt;span class="c"&gt;# Can container resolve service name?&lt;/span&gt;
docker &lt;span class="nb"&gt;exec &lt;/span&gt;myapp ping &lt;span class="nt"&gt;-c3&lt;/span&gt; db        &lt;span class="c"&gt;# Can container reach db?&lt;/span&gt;

&lt;span class="c"&gt;# ===== STEP 7: Docker Events =====&lt;/span&gt;
docker events &lt;span class="nt"&gt;--filter&lt;/span&gt; &lt;span class="s1"&gt;'container=myapp'&lt;/span&gt;
docker events &lt;span class="nt"&gt;--filter&lt;/span&gt; &lt;span class="s1"&gt;'container=myapp'&lt;/span&gt; &lt;span class="nt"&gt;--since&lt;/span&gt; 1h
&lt;span class="c"&gt;# Shows: die, kill, oom, start, stop, restart events&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Debugging Containers That Won't Start
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Container crashes immediately — override the entrypoint&lt;/span&gt;
docker run &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;--entrypoint&lt;/span&gt; /bin/sh myapp:latest
&lt;span class="c"&gt;# Or bash:&lt;/span&gt;
docker run &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;--entrypoint&lt;/span&gt; /bin/bash myapp:latest

&lt;span class="c"&gt;# Then manually run the CMD to see the exact error:&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;node server.js
&lt;span class="c"&gt;# Error: Cannot find module 'express'  ← npm install was never run!&lt;/span&gt;

&lt;span class="c"&gt;# Debug with a minimal command&lt;/span&gt;
docker run &lt;span class="nt"&gt;-it&lt;/span&gt; myapp:latest &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"container works"&lt;/span&gt;
docker run &lt;span class="nt"&gt;-it&lt;/span&gt; myapp:latest &lt;span class="nb"&gt;ls&lt;/span&gt; /app    &lt;span class="c"&gt;# Check if files are present&lt;/span&gt;
docker run &lt;span class="nt"&gt;-it&lt;/span&gt; myapp:latest &lt;span class="nb"&gt;env&lt;/span&gt;        &lt;span class="c"&gt;# Check env vars&lt;/span&gt;

&lt;span class="c"&gt;# Check image layers/history for clues&lt;/span&gt;
docker &lt;span class="nb"&gt;history &lt;/span&gt;myapp:latest
docker inspect myapp:latest | jq &lt;span class="s1"&gt;'.[0].Config'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Advanced Network Debugging — netshoot
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# nicolaka/netshoot — network debugging toolkit&lt;/span&gt;
&lt;span class="c"&gt;# Contains: curl, wget, dig, nslookup, netstat, tcpdump, etc.&lt;/span&gt;

&lt;span class="c"&gt;# Share network namespace with target container&lt;/span&gt;
docker run &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--net&lt;/span&gt; container:myapp &lt;span class="se"&gt;\&lt;/span&gt;
  nicolaka/netshoot

&lt;span class="c"&gt;# Now you have full network tools in myapp's network namespace&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;netstat &lt;span class="nt"&gt;-tulpn&lt;/span&gt;      &lt;span class="c"&gt;# See what myapp is listening on&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;curl localhost:3000 &lt;span class="c"&gt;# Test myapp's API&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;tcpdump &lt;span class="nt"&gt;-i&lt;/span&gt; eth0     &lt;span class="c"&gt;# Capture traffic&lt;/span&gt;

&lt;span class="c"&gt;# Share both network AND pid namespace&lt;/span&gt;
docker run &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--net&lt;/span&gt; container:myapp &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--pid&lt;/span&gt; container:myapp &lt;span class="se"&gt;\&lt;/span&gt;
  nicolaka/netshoot

&lt;span class="c"&gt;# Check for OOM kills in system logs&lt;/span&gt;
dmesg | &lt;span class="nb"&gt;grep &lt;/span&gt;oom
journalctl &lt;span class="nt"&gt;-k&lt;/span&gt; | &lt;span class="nb"&gt;grep &lt;/span&gt;oom | &lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-20&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Decoding exit codes — &lt;code&gt;Exited (0)&lt;/code&gt;: clean exit (intentional). &lt;code&gt;Exited (1)&lt;/code&gt;: application error. &lt;code&gt;Exited (137)&lt;/code&gt;: killed by OOM killer (memory limit exceeded). &lt;code&gt;Exited (139)&lt;/code&gt;: segfault. &lt;code&gt;Exited (143)&lt;/code&gt;: SIGTERM received (graceful stop). Exit codes reveal a lot before you even read the logs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; Alpine base images do not include bash, curl, or netstat by default. Install them temporarily for debugging: &lt;code&gt;docker exec myapp apk add --no-cache curl bash net-tools&lt;/code&gt;. For distroless images, use ephemeral debug containers (a Kubernetes feature).&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Docker Health Checks — Container Self-Assessment
&lt;/h2&gt;

&lt;p&gt;A running container does not mean the application is healthy. The process may be alive but unable to connect to the database, or not listening on the expected port. Health checks tell Docker whether the app is actually working.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Health check states — &lt;code&gt;starting&lt;/code&gt;: within the start_period, checks are ignored. &lt;code&gt;healthy&lt;/code&gt;: the most recent check passed. &lt;code&gt;unhealthy&lt;/code&gt;: retries exhausted, all checks failed. Docker Swarm and Kubernetes automatically restart unhealthy containers.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Dockerfile Health Check Options
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# HTTP endpoint check (for REST APIs)&lt;/span&gt;
&lt;span class="k"&gt;HEALTHCHECK&lt;/span&gt;&lt;span class="s"&gt; --interval=30s --timeout=5s --start-period=30s --retries=3 \&lt;/span&gt;
  CMD curl -f http://localhost:3000/health || exit 1
&lt;span class="c"&gt;# --interval: How often to check (default: 30s)&lt;/span&gt;
&lt;span class="c"&gt;# --timeout: How long to wait for one check (default: 30s)&lt;/span&gt;
&lt;span class="c"&gt;# --start-period: Startup grace period — failed checks don't count (default: 0s)&lt;/span&gt;
&lt;span class="c"&gt;# --retries: How many times to retry before marking unhealthy (default: 3)&lt;/span&gt;
&lt;span class="c"&gt;# curl -f: Fails silently on HTTP errors (4xx, 5xx)&lt;/span&gt;

&lt;span class="c"&gt;# TCP port check (for databases or non-HTTP services)&lt;/span&gt;
&lt;span class="k"&gt;HEALTHCHECK&lt;/span&gt;&lt;span class="s"&gt; --interval=30s --timeout=3s \&lt;/span&gt;
  CMD nc -z localhost 5432 || exit 1
&lt;span class="c"&gt;# nc -z: Check if port is open (no data sent)&lt;/span&gt;

&lt;span class="c"&gt;# Node.js application check (when curl is not available)&lt;/span&gt;
&lt;span class="k"&gt;HEALTHCHECK&lt;/span&gt;&lt;span class="s"&gt; --interval=30s --timeout=5s --start-period=30s --retries=3 \&lt;/span&gt;
  CMD node -e "require('http').get('http://localhost:3000/health', (r) =&amp;gt; { process.exit(r.statusCode === 200 ? 0 : 1) })" || exit 1

&lt;span class="c"&gt;# Custom script (for complex checks)&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; healthcheck.sh /usr/local/bin/healthcheck&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x /usr/local/bin/healthcheck
&lt;span class="k"&gt;HEALTHCHECK&lt;/span&gt;&lt;span class="s"&gt; --interval=30s --timeout=10s --retries=3 \&lt;/span&gt;
  CMD /usr/local/bin/healthcheck

&lt;span class="c"&gt;# Disable inherited healthcheck (from base image)&lt;/span&gt;
&lt;span class="k"&gt;HEALTHCHECK&lt;/span&gt;&lt;span class="s"&gt; NONE&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Complex Health Check Script
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/sh&lt;/span&gt;
&lt;span class="c"&gt;# healthcheck.sh — Multiple checks in one script&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;

&lt;span class="c"&gt;# Check 1: HTTP endpoint response&lt;/span&gt;
&lt;span class="nv"&gt;http_status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-sL&lt;/span&gt; &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s2"&gt;"%{http_code}"&lt;/span&gt; http://localhost:3000/health &lt;span class="nt"&gt;-o&lt;/span&gt; /dev/null &lt;span class="nt"&gt;--max-time&lt;/span&gt; 3&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$http_status&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s2"&gt;"200"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"UNHEALTHY: HTTP check failed with status &lt;/span&gt;&lt;span class="nv"&gt;$http_status&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# Check 2: Database connectivity&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; nc &lt;span class="nt"&gt;-z&lt;/span&gt; db 5432 2&amp;gt;/dev/null&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"UNHEALTHY: Cannot reach database on port 5432"&lt;/span&gt;
  &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# Check 3: Disk space (prevent crash due to full disk)&lt;/span&gt;
&lt;span class="nv"&gt;disk_usage&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;df&lt;/span&gt; /app | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'NR==2 {print $5}'&lt;/span&gt; | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s1"&gt;'s/%//'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$disk_usage&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-gt&lt;/span&gt; 90 &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"UNHEALTHY: Disk usage too high: &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;disk_usage&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;%"&lt;/span&gt;
  &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"HEALTHY: All checks passed"&lt;/span&gt;
&lt;span class="nb"&gt;exit &lt;/span&gt;0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Health Check in Docker Compose
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3.8"&lt;/span&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;db&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres:15-alpine&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;secret&lt;/span&gt;
    &lt;span class="na"&gt;healthcheck&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CMD-SHELL"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pg_isready&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-U&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;postgres"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10s&lt;/span&gt;
      &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;5s&lt;/span&gt;
      &lt;span class="na"&gt;retries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
      &lt;span class="na"&gt;start_period&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;30s&lt;/span&gt;   &lt;span class="c1"&gt;# PostgreSQL takes time to start!&lt;/span&gt;

  &lt;span class="na"&gt;api&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myapi:latest&lt;/span&gt;
    &lt;span class="na"&gt;healthcheck&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CMD"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;curl"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-f"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:3000/health"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;30s&lt;/span&gt;
      &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;5s&lt;/span&gt;
      &lt;span class="na"&gt;retries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
      &lt;span class="na"&gt;start_period&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;40s&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;db&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;service_healthy&lt;/span&gt;  &lt;span class="c1"&gt;# Wait for db to be HEALTHY, not just running!&lt;/span&gt;

  &lt;span class="na"&gt;worker&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myworker:latest&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;api&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;service_healthy&lt;/span&gt;  &lt;span class="c1"&gt;# Start worker only when api is healthy&lt;/span&gt;
      &lt;span class="na"&gt;db&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;service_healthy&lt;/span&gt;

&lt;span class="c1"&gt;# depends_on with service_healthy = proper startup ordering!&lt;/span&gt;
&lt;span class="c1"&gt;# Without this: api starts before db is ready → connection error!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Checking Health Status
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# docker ps shows health status&lt;/span&gt;
docker ps
&lt;span class="c"&gt;# CONTAINER ID  IMAGE    STATUS&lt;/span&gt;
&lt;span class="c"&gt;# abc123        myapp    Up 5 minutes (healthy)&lt;/span&gt;
&lt;span class="c"&gt;# def456        mydb     Up 2 minutes (starting)&lt;/span&gt;
&lt;span class="c"&gt;# ghi789        worker   Up 10 minutes (unhealthy)  ← Problem!&lt;/span&gt;

&lt;span class="c"&gt;# Get health status&lt;/span&gt;
docker inspect &lt;span class="nt"&gt;--format&lt;/span&gt; &lt;span class="s1"&gt;'{{.State.Health.Status}}'&lt;/span&gt; myapp
&lt;span class="c"&gt;# healthy&lt;/span&gt;

&lt;span class="c"&gt;# Detailed health info with history&lt;/span&gt;
docker inspect &lt;span class="nt"&gt;--format&lt;/span&gt; &lt;span class="s1"&gt;'{{json .State.Health}}'&lt;/span&gt; myapp | jq
&lt;span class="c"&gt;# {&lt;/span&gt;
&lt;span class="c"&gt;#   "Status": "healthy",&lt;/span&gt;
&lt;span class="c"&gt;#   "FailingStreak": 0,&lt;/span&gt;
&lt;span class="c"&gt;#   "Log": [&lt;/span&gt;
&lt;span class="c"&gt;#     {&lt;/span&gt;
&lt;span class="c"&gt;#       "Start": "2024-01-15T...",&lt;/span&gt;
&lt;span class="c"&gt;#       "End": "2024-01-15T...",&lt;/span&gt;
&lt;span class="c"&gt;#       "ExitCode": 0,&lt;/span&gt;
&lt;span class="c"&gt;#       "Output": "HEALTHY: All checks passed"&lt;/span&gt;
&lt;span class="c"&gt;#     }&lt;/span&gt;
&lt;span class="c"&gt;#   ]&lt;/span&gt;
&lt;span class="c"&gt;# }&lt;/span&gt;

&lt;span class="c"&gt;# Wait for container to be healthy (useful in scripts)&lt;/span&gt;
&lt;span class="k"&gt;until&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;docker inspect &lt;span class="nt"&gt;--format&lt;/span&gt; &lt;span class="s1"&gt;'{{.State.Health.Status}}'&lt;/span&gt; myapp&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"healthy"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Waiting for myapp to be healthy..."&lt;/span&gt;
  &lt;span class="nb"&gt;sleep &lt;/span&gt;5
&lt;span class="k"&gt;done
&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"myapp is healthy!"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Design a dedicated &lt;code&gt;/health&lt;/code&gt; endpoint that returns HTTP 200 when healthy and 503 when unhealthy. It should check database connectivity, required external services, and application state. Response time should be under 100ms — the health check itself should not be slow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; If &lt;code&gt;start_period&lt;/code&gt; is too short and your app starts slowly, the container will immediately be marked "unhealthy" and enter a restart loop. Java apps may take 30–60 seconds. Node.js typically takes 5–10 seconds. Set the value based on your app's actual startup time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Docker's &lt;code&gt;HEALTHCHECK&lt;/code&gt; instruction does not work in Kubernetes — it is a Docker Compose feature. Kubernetes has its own &lt;code&gt;livenessProbe&lt;/code&gt;, &lt;code&gt;readinessProbe&lt;/code&gt;, and &lt;code&gt;startupProbe&lt;/code&gt;. The concept is the same, but the syntax differs. When migrating to Kubernetes, convert Dockerfile HEALTHCHECKs to Kubernetes probes.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Development Workflows with Docker
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Docker for Local Development — The Real Game Changer
&lt;/h3&gt;

&lt;p&gt;Docker development environments ensure every developer — whether on Windows, Mac, or Linux — runs the exact same stack. New developer onboarding becomes: &lt;code&gt;git clone &amp;amp;&amp;amp; docker compose up&lt;/code&gt; — done in under 5 minutes.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Docker dev environments eliminate "works on my machine" issues permanently. Every developer gets an identical stack, regardless of operating system.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# docker-compose.dev.yml - Development setup&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;api&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
      &lt;span class="na"&gt;dockerfile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Dockerfile.dev&lt;/span&gt;  &lt;span class="c1"&gt;# Dev dockerfile with hot reload&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.:/app&lt;/span&gt;                    &lt;span class="c1"&gt;# Mount source code!&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/app/node_modules&lt;/span&gt;         &lt;span class="c1"&gt;# Keep container's node_modules&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;NODE_ENV&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;development&lt;/span&gt;
      &lt;span class="na"&gt;DEBUG&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;true"&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;9229:9229"&lt;/span&gt;               &lt;span class="c1"&gt;# Node.js debug port&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm run dev&lt;/span&gt;          &lt;span class="c1"&gt;# Hot reload command&lt;/span&gt;

  &lt;span class="c1"&gt;# Development tools&lt;/span&gt;
  &lt;span class="na"&gt;mailhog&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mailhog/mailhog&lt;/span&gt;        &lt;span class="c1"&gt;# Email testing&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8025:8025"&lt;/span&gt;               &lt;span class="c1"&gt;# Web UI&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1025:1025"&lt;/span&gt;               &lt;span class="c1"&gt;# SMTP&lt;/span&gt;
    &lt;span class="na"&gt;profiles&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;dev-tools&lt;/span&gt;

  &lt;span class="na"&gt;redis-commander&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rediscommander/redis-commander&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;REDIS_HOSTS&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;local:redis:6379"&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8081:8081"&lt;/span&gt;
    &lt;span class="na"&gt;profiles&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;dev-tools&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use profiles to start dev tools only when needed: &lt;code&gt;docker compose --profile dev-tools up&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Dockerfile.dev — Node.js with Hot Reload
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Dockerfile.dev - Development only&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:18-alpine&lt;/span&gt;

&lt;span class="c"&gt;# Install nodemon for hot reload&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; nodemon

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="c"&gt;# Copy package files&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;  &lt;span class="c"&gt;# All deps including devDependencies&lt;/span&gt;

&lt;span class="c"&gt;# Don't COPY source - it's mounted via volume!&lt;/span&gt;

&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 3000 9229&lt;/span&gt;

&lt;span class="c"&gt;# Hot reload with nodemon&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["nodemon", "--inspect=0.0.0.0:9229", "src/server.js"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Development vs Production — Key Differences
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Development&lt;/th&gt;
&lt;th&gt;Production&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Base Image&lt;/td&gt;
&lt;td&gt;node:18 (full)&lt;/td&gt;
&lt;td&gt;node:18-alpine&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;devDependencies&lt;/td&gt;
&lt;td&gt;Installed&lt;/td&gt;
&lt;td&gt;Excluded&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Source Code&lt;/td&gt;
&lt;td&gt;Via bind mount&lt;/td&gt;
&lt;td&gt;COPYed into image&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hot Reload&lt;/td&gt;
&lt;td&gt;nodemon&lt;/td&gt;
&lt;td&gt;Not needed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Debug Ports&lt;/td&gt;
&lt;td&gt;9229 exposed&lt;/td&gt;
&lt;td&gt;No debug&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Image Size&lt;/td&gt;
&lt;td&gt;~1GB acceptable&lt;/td&gt;
&lt;td&gt;&amp;lt; 200MB target&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi-stage Build&lt;/td&gt;
&lt;td&gt;Not needed&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Benefits of Docker for Development
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Same Environment&lt;/strong&gt;: Every developer runs an identical stack. "Works on my machine" becomes irrelevant.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Instant Onboarding&lt;/strong&gt;: New developer joins: &lt;code&gt;git clone &amp;amp;&amp;amp; docker compose up&lt;/code&gt; = fully running stack in 5 minutes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No Version Conflicts&lt;/strong&gt;: Switch between Node 16, 18, 20 projects without juggling version managers. Each project is isolated.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Exact Production Parity&lt;/strong&gt;: Test with the exact same database version (Postgres 16, MongoDB 7) as production.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Docker for React/Node Development
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This section covers CRA, Vite, Next.js, and full MERN stack setups with Docker, including real-world pitfalls that trip up beginners.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  1. Dockerize Create React App (CRA) with Hot Reload
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Dockerfile.dev for React CRA&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:18-alpine&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;span class="c"&gt;# Don't copy source - mounted via volume&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 3000&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["npm", "start"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# docker-compose.yml for React dev&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;react-app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
      &lt;span class="na"&gt;dockerfile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Dockerfile.dev&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.:/app&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/app/node_modules&lt;/span&gt;          &lt;span class="c1"&gt;# Prevent overwriting container's node_modules!&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3000:3000"&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;CHOKIDAR_USEPOLLING=true&lt;/span&gt;   &lt;span class="c1"&gt;# Required for file watching in Docker!&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;REACT_APP_API_URL=http://localhost:5000&lt;/span&gt;
    &lt;span class="na"&gt;stdin_open&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;               &lt;span class="c1"&gt;# Required for CRA&lt;/span&gt;
    &lt;span class="na"&gt;tty&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Dockerize Vite React App
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; Vite by default only listens on &lt;code&gt;localhost&lt;/code&gt; inside the container. The &lt;code&gt;--host 0.0.0.0&lt;/code&gt; flag is required to access it from outside the container.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Dockerfile.dev for Vite&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:18-alpine&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 5173&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["npm", "run", "dev", "--", "--host", "0.0.0.0"]&lt;/span&gt;
&lt;span class="c"&gt;# --host 0.0.0.0 is CRITICAL! Otherwise Vite only listens on localhost inside container&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;vite-app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
      &lt;span class="na"&gt;dockerfile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Dockerfile.dev&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.:/app&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/app/node_modules&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;5173:5173"&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;VITE_API_URL=http://localhost:3001&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Production React App with Nginx
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Dockerfile.prod - Complete production build&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;node:18-alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;builder&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; REACT_APP_API_URL&lt;/span&gt;
&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; REACT_APP_ENV=production&lt;/span&gt;

&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; REACT_APP_API_URL=$REACT_APP_API_URL&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; REACT_APP_ENV=$REACT_APP_ENV&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm ci

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm run build

&lt;span class="c"&gt;# Production: Nginx serving&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; nginx:1.25-alpine&lt;/span&gt;

&lt;span class="c"&gt;# Custom nginx config for React SPA&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; nginx.conf /etc/nginx/conf.d/default.conf&lt;/span&gt;

&lt;span class="c"&gt;# Copy built React app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /app/build /usr/share/nginx/html&lt;/span&gt;

&lt;span class="c"&gt;# Health endpoint&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"healthy"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /usr/share/nginx/html/health

&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 80&lt;/span&gt;

&lt;span class="k"&gt;HEALTHCHECK&lt;/span&gt;&lt;span class="s"&gt; --interval=30s --timeout=3s \&lt;/span&gt;
  CMD wget -qO- http://localhost/health || exit 1

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["nginx", "-g", "daemon off;"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;root&lt;/span&gt; &lt;span class="n"&gt;/usr/share/nginx/html&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;index&lt;/span&gt; &lt;span class="s"&gt;index.html&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;# React Router support — REQUIRED for SPAs!&lt;/span&gt;
    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;try_files&lt;/span&gt; &lt;span class="nv"&gt;$uri&lt;/span&gt; &lt;span class="nv"&gt;$uri&lt;/span&gt;&lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="n"&gt;/index.html&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;# API proxy (optional)&lt;/span&gt;
    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/api/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://api:5000/&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Host&lt;/span&gt; &lt;span class="nv"&gt;$host&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Real-IP&lt;/span&gt; &lt;span class="nv"&gt;$remote_addr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;# Cache static assets aggressively&lt;/span&gt;
    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="p"&gt;~&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="s"&gt;.(js|css|png|jpg|jpeg|gif|ico|svg|woff2)&lt;/span&gt;$ &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;expires&lt;/span&gt; &lt;span class="s"&gt;1y&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;add_header&lt;/span&gt; &lt;span class="s"&gt;Cache-Control&lt;/span&gt; &lt;span class="s"&gt;"public,&lt;/span&gt; &lt;span class="s"&gt;immutable"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;# Gzip compression&lt;/span&gt;
    &lt;span class="kn"&gt;gzip&lt;/span&gt; &lt;span class="no"&gt;on&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;gzip_vary&lt;/span&gt; &lt;span class="no"&gt;on&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;gzip_types&lt;/span&gt; &lt;span class="nc"&gt;text/plain&lt;/span&gt; &lt;span class="nc"&gt;text/css&lt;/span&gt; &lt;span class="nc"&gt;application/json&lt;/span&gt; &lt;span class="nc"&gt;application/javascript&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Next.js Production — Official Recommended Dockerfile
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;node:18-alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;base&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apk add &lt;span class="nt"&gt;--no-cache&lt;/span&gt; libc6-compat
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;base&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;deps&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm ci

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;base&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;builder&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=deps /app/node_modules ./node_modules&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; NEXT_TELEMETRY_DISABLED=1&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; NODE_ENV=production&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm run build

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;base&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;runner&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; NODE_ENV=production&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; NEXT_TELEMETRY_DISABLED=1&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;addgroup &lt;span class="nt"&gt;--system&lt;/span&gt; &lt;span class="nt"&gt;--gid&lt;/span&gt; 1001 nodejs &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; adduser &lt;span class="nt"&gt;--system&lt;/span&gt; &lt;span class="nt"&gt;--uid&lt;/span&gt; 1001 nextjs
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /app/public ./public&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder --chown=nextjs:nodejs /app/.next/standalone ./&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static&lt;/span&gt;
&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; nextjs&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 3000&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["node", "server.js"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Full MERN Stack Development Setup
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# docker-compose.yml - Full MERN Dev Stack&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# React Frontend&lt;/span&gt;
  &lt;span class="na"&gt;frontend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./frontend&lt;/span&gt;
      &lt;span class="na"&gt;dockerfile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Dockerfile.dev&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./frontend:/app&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/app/node_modules&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3000:3000"&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;REACT_APP_API_URL=http://localhost:5000&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;CHOKIDAR_USEPOLLING=true&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;api&lt;/span&gt;

  &lt;span class="c1"&gt;# Node.js API&lt;/span&gt;
  &lt;span class="na"&gt;api&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./api&lt;/span&gt;
      &lt;span class="na"&gt;dockerfile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Dockerfile.dev&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./api:/app&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/app/node_modules&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;5000:5000"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;9229:9229"&lt;/span&gt;   &lt;span class="c1"&gt;# Debug port&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;NODE_ENV&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;development&lt;/span&gt;
      &lt;span class="na"&gt;MONGODB_URI&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mongodb://mongodb:27017/myapp&lt;/span&gt;
      &lt;span class="na"&gt;JWT_SECRET&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dev-secret-key&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;mongodb&lt;/span&gt;

  &lt;span class="c1"&gt;# MongoDB&lt;/span&gt;
  &lt;span class="na"&gt;mongodb&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mongo:7&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;27017:27017"&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;mongo_data:/data/db&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;MONGO_INITDB_DATABASE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myapp&lt;/span&gt;

  &lt;span class="c1"&gt;# Redis cache&lt;/span&gt;
  &lt;span class="na"&gt;redis&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis:7-alpine&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;6379:6379"&lt;/span&gt;

  &lt;span class="c1"&gt;# Mongo Express - GUI for MongoDB (localhost:8081)&lt;/span&gt;
  &lt;span class="na"&gt;mongo-express&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mongo-express&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8081:8081"&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;ME_CONFIG_MONGODB_SERVER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mongodb&lt;/span&gt;
      &lt;span class="na"&gt;ME_CONFIG_BASICAUTH_USERNAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;admin&lt;/span&gt;
      &lt;span class="na"&gt;ME_CONFIG_BASICAUTH_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;admin&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;mongodb&lt;/span&gt;

&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;mongo_data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Common React + Docker Problems and Fixes
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Problem&lt;/th&gt;
&lt;th&gt;Cause&lt;/th&gt;
&lt;th&gt;Fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Hot reload not working&lt;/td&gt;
&lt;td&gt;File watching broken in Docker&lt;/td&gt;
&lt;td&gt;Set &lt;code&gt;CHOKIDAR_USEPOLLING=true&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Can't access Vite app&lt;/td&gt;
&lt;td&gt;Vite binding to localhost only&lt;/td&gt;
&lt;td&gt;Add &lt;code&gt;--host 0.0.0.0&lt;/code&gt; to dev command&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;node_modules overwritten&lt;/td&gt;
&lt;td&gt;Host volume masks container deps&lt;/td&gt;
&lt;td&gt;Add &lt;code&gt;/app/node_modules&lt;/code&gt; as anonymous volume&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Build fails: env vars missing&lt;/td&gt;
&lt;td&gt;REACT_APP_ prefix not passed at build time&lt;/td&gt;
&lt;td&gt;Use &lt;code&gt;ARG&lt;/code&gt; + &lt;code&gt;ENV&lt;/code&gt; in Dockerfile&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Very slow Docker builds&lt;/td&gt;
&lt;td&gt;Large build context with node_modules&lt;/td&gt;
&lt;td&gt;Add &lt;code&gt;node_modules&lt;/code&gt; to &lt;code&gt;.dockerignore&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Debugging Dockerized Apps
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Node.js Remote Debugging
&lt;/h3&gt;

&lt;p&gt;You can debug a Node.js app running inside a container using Chrome DevTools or VS Code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Start Node.js container in debug mode&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; api-debug &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-p&lt;/span&gt; 9229:9229 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;:/app &lt;span class="se"&gt;\&lt;/span&gt;
  node:18 &lt;span class="se"&gt;\&lt;/span&gt;
  node &lt;span class="nt"&gt;--inspect&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0.0.0.0:9229 src/server.js

&lt;span class="c"&gt;# Then open Chrome: chrome://inspect&lt;/span&gt;
&lt;span class="c"&gt;# Your server will appear under "Remote Target"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;VS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Code&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;launch.json&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"configurations"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"request"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"attach"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Docker: Attach to Node"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"port"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;9229&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"address"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"localhost"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"localRoot"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"${workspaceFolder}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"remoteRoot"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/app"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"restart"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Python Debugging with debugpy
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Python debugging with debugpy&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-p&lt;/span&gt; 5678:5678 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;:/app &lt;span class="se"&gt;\&lt;/span&gt;
  python:3.11 &lt;span class="se"&gt;\&lt;/span&gt;
  python &lt;span class="nt"&gt;-m&lt;/span&gt; debugpy &lt;span class="nt"&gt;--listen&lt;/span&gt; 0.0.0.0:5678 &lt;span class="nt"&gt;--wait-for-client&lt;/span&gt; app.py

&lt;span class="c"&gt;# In VS Code, use the Python: Remote Attach configuration&lt;/span&gt;
&lt;span class="c"&gt;# Port: 5678, Host: localhost&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Network Debugging with netshoot
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; &lt;code&gt;nicolaka/netshoot&lt;/code&gt; contains curl, tcpdump, netstat, nslookup, traceroute, dig — everything you need. Attach to any container's network namespace and debug immediately.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Network debugging with netshoot&lt;/span&gt;
docker run &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--network&lt;/span&gt; container:myapp &lt;span class="se"&gt;\&lt;/span&gt;
  nicolaka/netshoot

&lt;span class="c"&gt;# Now inside myapp's network namespace with full tooling:&lt;/span&gt;
&lt;span class="c"&gt;# curl, tcpdump, netstat, nslookup, traceroute, dig, etc.&lt;/span&gt;

&lt;span class="c"&gt;# Check DNS resolution inside container network&lt;/span&gt;
docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;--network&lt;/span&gt; mynetwork nicolaka/netshoot nslookup api

&lt;span class="c"&gt;# Check if port is reachable between services&lt;/span&gt;
docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;--network&lt;/span&gt; mynetwork nicolaka/netshoot nc &lt;span class="nt"&gt;-zv&lt;/span&gt; api 5000

&lt;span class="c"&gt;# Capture TCP traffic&lt;/span&gt;
docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;--network&lt;/span&gt; container:myapp nicolaka/netshoot tcpdump &lt;span class="nt"&gt;-i&lt;/span&gt; eth0 port 5000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  General Container Debugging Commands
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Open a shell inside the container&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; myapp sh

&lt;span class="c"&gt;# View logs with timestamps&lt;/span&gt;
docker logs &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="nt"&gt;--timestamps&lt;/span&gt; myapp

&lt;span class="c"&gt;# Process list inside container&lt;/span&gt;
docker &lt;span class="nb"&gt;exec &lt;/span&gt;myapp ps aux

&lt;span class="c"&gt;# Check environment variables&lt;/span&gt;
docker &lt;span class="nb"&gt;exec &lt;/span&gt;myapp &lt;span class="nb"&gt;env&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; api

&lt;span class="c"&gt;# Full container config&lt;/span&gt;
docker inspect myapp | jq &lt;span class="s1"&gt;'.[0].NetworkSettings'&lt;/span&gt;

&lt;span class="c"&gt;# Browse the filesystem&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; myapp find /app &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"*.log"&lt;/span&gt;

&lt;span class="c"&gt;# Copy files out of container for inspection&lt;/span&gt;
docker &lt;span class="nb"&gt;cp &lt;/span&gt;myapp:/app/logs/error.log ./error.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Production Readiness Checklist
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; Before going to production, verify all items below. Common mistakes in production Dockerfiles and compose files create security vulnerabilities, crashes, or performance issues. Bookmark this checklist.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Production Dockerfile Checklist
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Check&lt;/th&gt;
&lt;th&gt;Why It Matters&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Specific base image version (not &lt;code&gt;latest&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Reproducible builds, no surprise updates&lt;/td&gt;
&lt;td&gt;☐&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi-stage build used&lt;/td&gt;
&lt;td&gt;Smaller final image, no build tools in prod&lt;/td&gt;
&lt;td&gt;☐&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Non-root user configured&lt;/td&gt;
&lt;td&gt;Security — limits blast radius of a breach&lt;/td&gt;
&lt;td&gt;☐&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;.dockerignore present&lt;/td&gt;
&lt;td&gt;Smaller build context, no secrets leaked&lt;/td&gt;
&lt;td&gt;☐&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HEALTHCHECK configured&lt;/td&gt;
&lt;td&gt;Orchestrators can detect unhealthy containers&lt;/td&gt;
&lt;td&gt;☐&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WORKDIR explicitly set&lt;/td&gt;
&lt;td&gt;Predictable file paths&lt;/td&gt;
&lt;td&gt;☐&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;package*.json COPY before source COPY&lt;/td&gt;
&lt;td&gt;Better layer caching&lt;/td&gt;
&lt;td&gt;☐&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No secrets in image/Dockerfile&lt;/td&gt;
&lt;td&gt;Security — images can be inspected&lt;/td&gt;
&lt;td&gt;☐&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Image size &amp;lt; 500MB (ideally &amp;lt; 200MB)&lt;/td&gt;
&lt;td&gt;Faster pulls, lower storage cost&lt;/td&gt;
&lt;td&gt;☐&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LABEL with version/maintainer info&lt;/td&gt;
&lt;td&gt;Traceability in production&lt;/td&gt;
&lt;td&gt;☐&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Graceful shutdown handled (SIGTERM)&lt;/td&gt;
&lt;td&gt;Zero-downtime deployments&lt;/td&gt;
&lt;td&gt;☐&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Graceful Shutdown in Node.js
&lt;/h3&gt;

&lt;p&gt;When Docker stops a container, it sends &lt;code&gt;SIGTERM&lt;/code&gt; first, then &lt;code&gt;SIGKILL&lt;/code&gt; after 10 seconds. If you do not handle SIGTERM, in-flight requests are dropped.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// server.js - Handle SIGTERM for graceful shutdown&lt;/span&gt;
&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SIGTERM&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SIGTERM received, shutting down gracefully&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;HTTP server closed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// Close DB connections&lt;/span&gt;
    &lt;span class="nx"&gt;mongoose&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;MongoDB disconnected&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// Force exit after 30 seconds&lt;/span&gt;
  &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Could not close connections in time, forcing exit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;30000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Image Versioning Strategy
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Tagging strategy for production&lt;/span&gt;
&lt;span class="nv"&gt;IMAGE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"myapp"&lt;/span&gt;
&lt;span class="nv"&gt;REGISTRY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"123456789.dkr.ecr.ap-south-1.amazonaws.com"&lt;/span&gt;
&lt;span class="nv"&gt;GIT_TAG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git describe &lt;span class="nt"&gt;--tags&lt;/span&gt; &lt;span class="nt"&gt;--always&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;GIT_SHA&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git rev-parse &lt;span class="nt"&gt;--short&lt;/span&gt; HEAD&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;DATE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%Y%m%d&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Always push with multiple tags&lt;/span&gt;
docker build &lt;span class="nt"&gt;-t&lt;/span&gt; &lt;span class="nv"&gt;$IMAGE&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
docker tag &lt;span class="nv"&gt;$IMAGE&lt;/span&gt; &lt;span class="nv"&gt;$REGISTRY&lt;/span&gt;/&lt;span class="nv"&gt;$IMAGE&lt;/span&gt;:&lt;span class="nv"&gt;$GIT_TAG&lt;/span&gt;       &lt;span class="c"&gt;# v1.2.3&lt;/span&gt;
docker tag &lt;span class="nv"&gt;$IMAGE&lt;/span&gt; &lt;span class="nv"&gt;$REGISTRY&lt;/span&gt;/&lt;span class="nv"&gt;$IMAGE&lt;/span&gt;:&lt;span class="nv"&gt;$GIT_SHA&lt;/span&gt;       &lt;span class="c"&gt;# abc1234&lt;/span&gt;
docker tag &lt;span class="nv"&gt;$IMAGE&lt;/span&gt; &lt;span class="nv"&gt;$REGISTRY&lt;/span&gt;/&lt;span class="nv"&gt;$IMAGE&lt;/span&gt;:&lt;span class="nv"&gt;$DATE&lt;/span&gt;          &lt;span class="c"&gt;# 20240115&lt;/span&gt;
docker tag &lt;span class="nv"&gt;$IMAGE&lt;/span&gt; &lt;span class="nv"&gt;$REGISTRY&lt;/span&gt;/&lt;span class="nv"&gt;$IMAGE&lt;/span&gt;:latest         &lt;span class="c"&gt;# latest (don't use in deploys!)&lt;/span&gt;

&lt;span class="c"&gt;# Deploy using a specific version, not latest!&lt;/span&gt;
docker pull &lt;span class="nv"&gt;$REGISTRY&lt;/span&gt;/&lt;span class="nv"&gt;$IMAGE&lt;/span&gt;:v1.2.3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; Never deploy using the &lt;code&gt;:latest&lt;/code&gt; tag in production. If someone pushes and the service restarts, an unexpected version may be deployed. Always use a git SHA or semantic version tag for deployments.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Docker with CI/CD Pipelines
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Complete GitHub Actions Workflow — Build, Test, Push
&lt;/h3&gt;

&lt;p&gt;Before writing your pipeline, validate your workflow syntax with the &lt;a href="https://opscanopy.com/github-actions-validator/" rel="noopener noreferrer"&gt;GitHub Actions Validator&lt;/a&gt; to catch errors early.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# .github/workflows/docker-build-push.yml&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build and Push Docker Image&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;v*'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;REGISTRY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ghcr.io&lt;/span&gt;
  &lt;span class="na"&gt;IMAGE_NAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.repository }}&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run tests&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;docker build --target test -t myapp:test .&lt;/span&gt;
          &lt;span class="s"&gt;docker run --rm myapp:test npm test&lt;/span&gt;

  &lt;span class="na"&gt;build-push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;github.event_name != 'pull_request'&lt;/span&gt;
    &lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;read&lt;/span&gt;
      &lt;span class="na"&gt;packages&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Setup Docker Buildx&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/setup-buildx-action@v3&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Login to GHCR&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/login-action@v3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;registry&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ env.REGISTRY }}&lt;/span&gt;
          &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.actor }}&lt;/span&gt;
          &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Extract metadata (tags, labels)&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;meta&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/metadata-action@v5&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;images&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}&lt;/span&gt;
          &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;type=semver,pattern={{version}}&lt;/span&gt;
            &lt;span class="s"&gt;type=semver,pattern={{major}}.{{minor}}&lt;/span&gt;
            &lt;span class="s"&gt;type=sha,prefix=sha-&lt;/span&gt;
            &lt;span class="s"&gt;type=raw,value=latest,enable={{is_default_branch}}&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build and push&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/build-push-action@v5&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
          &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
          &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ steps.meta.outputs.tags }}&lt;/span&gt;
          &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ steps.meta.outputs.labels }}&lt;/span&gt;
          &lt;span class="na"&gt;cache-from&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;type=gha&lt;/span&gt;          &lt;span class="c1"&gt;# Use GitHub Actions cache!&lt;/span&gt;
          &lt;span class="na"&gt;cache-to&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;type=gha,mode=max&lt;/span&gt;
          &lt;span class="na"&gt;platforms&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;linux/amd64,linux/arm64&lt;/span&gt;   &lt;span class="c1"&gt;# Multi-arch build&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deploy to AWS ECS via CI/CD
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;build-push&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Configure AWS credentials&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aws-actions/configure-aws-credentials@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;aws-access-key-id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.AWS_ACCESS_KEY_ID }}&lt;/span&gt;
          &lt;span class="na"&gt;aws-secret-access-key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.AWS_SECRET_ACCESS_KEY }}&lt;/span&gt;
          &lt;span class="na"&gt;aws-region&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ap-south-1&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Download task definition&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;aws ecs describe-task-definition \&lt;/span&gt;
            &lt;span class="s"&gt;--task-definition myapp \&lt;/span&gt;
            &lt;span class="s"&gt;--query taskDefinition &amp;gt; task-definition.json&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Update ECS task definition&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;task-def&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aws-actions/amazon-ecs-render-task-definition@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;task-definition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;task-definition.json&lt;/span&gt;
          &lt;span class="na"&gt;container-name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myapp&lt;/span&gt;
          &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy to ECS&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aws-actions/amazon-ecs-deploy-task-definition@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;task-definition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ steps.task-def.outputs.task-definition }}&lt;/span&gt;
          &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myapp-service&lt;/span&gt;
          &lt;span class="na"&gt;cluster&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;production&lt;/span&gt;
          &lt;span class="na"&gt;wait-for-service-stability&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;   &lt;span class="c1"&gt;# Wait for deploy to complete&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you are using GitLab CI instead, validate your &lt;code&gt;.gitlab-ci.yml&lt;/code&gt; syntax with the &lt;a href="https://opscanopy.com/gitlab-ci-validator/" rel="noopener noreferrer"&gt;GitLab CI Validator&lt;/a&gt; before pushing.&lt;/p&gt;

&lt;h3&gt;
  
  
  CI/CD Pipeline Flow
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Code Push&lt;/strong&gt;: Developer runs &lt;code&gt;git push&lt;/code&gt; → GitHub Actions triggers automatically.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test&lt;/strong&gt;: Docker image builds with test target → tests run inside the container.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build and Push&lt;/strong&gt;: Multi-arch production image builds and pushes to registry with proper tags.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deploy&lt;/strong&gt;: ECS task definition updates → service performs a rolling deploy with zero downtime.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; "Our CI/CD pipeline runs on GitHub Actions. Every push to main automatically builds the Docker image, runs tests inside the container, then pushes to ECR and does an ECS rolling deploy — all automated. Manual production deployments never happen."&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Monitoring Docker Containers
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Docker Built-in Monitoring Commands
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Real-time stats — CPU, Memory, Network, IO&lt;/span&gt;
docker stats                    &lt;span class="c"&gt;# All containers&lt;/span&gt;
docker stats myapp &lt;span class="nt"&gt;--no-stream&lt;/span&gt;  &lt;span class="c"&gt;# One-time snapshot (no live update)&lt;/span&gt;

&lt;span class="c"&gt;# Human-readable format&lt;/span&gt;
docker stats &lt;span class="nt"&gt;--format&lt;/span&gt; &lt;span class="s2"&gt;"table {{.Container}}&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s2"&gt;{{.CPUPerc}}&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s2"&gt;{{.MemUsage}}&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s2"&gt;{{.NetIO}}"&lt;/span&gt;

&lt;span class="c"&gt;# Events stream — what is happening in the system&lt;/span&gt;
docker events &lt;span class="nt"&gt;--since&lt;/span&gt; 1h

&lt;span class="c"&gt;# Filter events by type&lt;/span&gt;
docker events &lt;span class="nt"&gt;--filter&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;container &lt;span class="nt"&gt;--filter&lt;/span&gt; &lt;span class="nv"&gt;event&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;die

&lt;span class="c"&gt;# Logs with time range&lt;/span&gt;
docker logs &lt;span class="nt"&gt;--since&lt;/span&gt; 1h &lt;span class="nt"&gt;--until&lt;/span&gt; 30m myapp
docker logs &lt;span class="nt"&gt;--since&lt;/span&gt; &lt;span class="s2"&gt;"2024-01-15T10:00:00"&lt;/span&gt; myapp

&lt;span class="c"&gt;# Formatted logs&lt;/span&gt;
docker logs &lt;span class="nt"&gt;--timestamps&lt;/span&gt; myapp 2&amp;gt;&amp;amp;1 | &lt;span class="nb"&gt;grep &lt;/span&gt;ERROR
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Full Monitoring Stack: cAdvisor + Prometheus + Grafana
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# monitoring/docker-compose.monitoring.yml&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;prometheus&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;prom/prometheus:latest&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;9090:9090"&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./prometheus.yml:/etc/prometheus/prometheus.yml&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;prometheus_data:/prometheus&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--config.file=/etc/prometheus/prometheus.yml&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--storage.tsdb.retention.time=30d&lt;/span&gt;

  &lt;span class="na"&gt;grafana&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;grafana/grafana:latest&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3001:3000"&lt;/span&gt;             &lt;span class="c1"&gt;# Port 3000 reserved for app&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;GF_SECURITY_ADMIN_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${GRAFANA_PASSWORD:-admin123}&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;grafana_data:/var/lib/grafana&lt;/span&gt;

  &lt;span class="na"&gt;cadvisor&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gcr.io/cadvisor/cadvisor:latest&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/:/rootfs:ro&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/var/run:/var/run:ro&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/sys:/sys:ro&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/var/lib/docker/:/var/lib/docker:ro&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8080:8080"&lt;/span&gt;

&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;prometheus_data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;grafana_data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# prometheus.yml&lt;/span&gt;
&lt;span class="na"&gt;global&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;scrape_interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;15s&lt;/span&gt;

&lt;span class="na"&gt;scrape_configs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;job_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;docker'&lt;/span&gt;
    &lt;span class="na"&gt;static_configs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;targets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;cadvisor:8080'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;    &lt;span class="c1"&gt;# Container metrics&lt;/span&gt;

  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;job_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;myapp'&lt;/span&gt;
    &lt;span class="na"&gt;static_configs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;targets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;myapp:3000'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;metrics_path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/metrics'&lt;/span&gt;          &lt;span class="c1"&gt;# App must expose a /metrics endpoint&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What to Monitor
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CPU Usage&lt;/strong&gt;: Detect container CPU throttling. Consistently above 80% means scale out or raise resource limits.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory&lt;/strong&gt;: Detect memory leaks. Check for OOM kills with &lt;code&gt;docker events --filter event=oom&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Network I/O&lt;/strong&gt;: Detect unusual traffic patterns. Track high egress costs in AWS.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Container Restarts&lt;/strong&gt;: Frequent restarts indicate a crash loop. Check the RESTARTS column in &lt;code&gt;docker ps&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Docker Orchestration Introduction
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Docker Swarm vs Kubernetes
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Docker Swarm&lt;/th&gt;
&lt;th&gt;Kubernetes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Complexity&lt;/td&gt;
&lt;td&gt;Simple&lt;/td&gt;
&lt;td&gt;Complex&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Setup Time&lt;/td&gt;
&lt;td&gt;Minutes&lt;/td&gt;
&lt;td&gt;Hours/Days&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Auto-scaling&lt;/td&gt;
&lt;td&gt;Basic&lt;/td&gt;
&lt;td&gt;Advanced (HPA, VPA)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ecosystem&lt;/td&gt;
&lt;td&gt;Docker only&lt;/td&gt;
&lt;td&gt;Massive (CNCF)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rolling Updates&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Self-healing&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi-host&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Industry Adoption (2026)&lt;/td&gt;
&lt;td&gt;Declining/Legacy&lt;/td&gt;
&lt;td&gt;Dominant Standard&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cloud Native Support&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;td&gt;First-class&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Learning Curve&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Swarm is now mostly legacy. In 2026, Kubernetes is the industry standard. That said, mastering Docker Compose and core Docker concepts makes learning Kubernetes significantly easier — the concepts are the same, just with greater complexity.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  When Do You Need Orchestration?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Scale: 10+ Containers&lt;/strong&gt;: Docker Compose runs on a single machine. Deploying across multiple machines requires orchestration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;High Availability&lt;/strong&gt;: If a node dies and the app automatically shifts to another node — that is orchestration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auto-scaling&lt;/strong&gt;: Automatically scale containers up when traffic increases and scale down when it drops.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero-downtime Deploys&lt;/strong&gt;: Rolling deployments where the old version keeps running until the new version is healthy.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Transition Path
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Learning path: Docker basics → Docker Compose (multi-container) → Kubernetes (multi-host, production-grade). Each step builds on the previous one. Master Docker concepts and Kubernetes will feel natural. When you are ready to take the next step, the &lt;a href="https://opscanopy.com/learn/guides/kubernetes-for-devops/" rel="noopener noreferrer"&gt;Kubernetes for DevOps&lt;/a&gt; guide picks up exactly where containers leave off.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Docker Swarm quick demo (educational only)&lt;/span&gt;
docker swarm init                       &lt;span class="c"&gt;# Initialize swarm on current node&lt;/span&gt;
docker service create &lt;span class="nt"&gt;--name&lt;/span&gt; web nginx  &lt;span class="c"&gt;# Create a service&lt;/span&gt;
docker service scale &lt;span class="nv"&gt;web&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5              &lt;span class="c"&gt;# Scale to 5 replicas (auto-distributed!)&lt;/span&gt;
docker service &lt;span class="nb"&gt;ls&lt;/span&gt;                       &lt;span class="c"&gt;# List services&lt;/span&gt;
docker service ps web                   &lt;span class="c"&gt;# See where each replica runs&lt;/span&gt;
docker swarm leave &lt;span class="nt"&gt;--force&lt;/span&gt;              &lt;span class="c"&gt;# Leave swarm&lt;/span&gt;

&lt;span class="c"&gt;# Equivalent nginx deployment in Kubernetes&lt;/span&gt;
kubectl create deployment web &lt;span class="nt"&gt;--image&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;nginx
kubectl scale deployment web &lt;span class="nt"&gt;--replicas&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5
kubectl get deployments
kubectl get pods                        &lt;span class="c"&gt;# See 5 pods running&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  AWS Container Services
&lt;/h2&gt;

&lt;h3&gt;
  
  
  AWS Container Services Overview
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Service&lt;/th&gt;
&lt;th&gt;What It Is&lt;/th&gt;
&lt;th&gt;When to Use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ECR&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Elastic Container Registry — private image registry&lt;/td&gt;
&lt;td&gt;Store Docker images for AWS deployments&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ECS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Managed container orchestration service&lt;/td&gt;
&lt;td&gt;Production workloads, teams familiar with Docker&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Fargate&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Serverless containers — no EC2 management&lt;/td&gt;
&lt;td&gt;Simple deployments, variable load, no infrastructure operations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;EKS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Managed Kubernetes service&lt;/td&gt;
&lt;td&gt;Complex microservices, existing Kubernetes expertise&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;App Runner&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Fully managed container platform&lt;/td&gt;
&lt;td&gt;Simple web apps, fastest time-to-deploy&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  ECS Architecture
&lt;/h3&gt;

&lt;p&gt;Understanding how ECS maps to Docker concepts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Task Definition&lt;/strong&gt; — Blueprint for container configuration, similar to a &lt;code&gt;docker-compose.yml&lt;/code&gt;. Defines image, ports, environment variables, CPU and memory limits.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Task&lt;/strong&gt; — A running instance of a task definition. Equivalent to a running container or group of containers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Service&lt;/strong&gt; — Maintains the desired task count, automatically replaces failed tasks, and integrates with a load balancer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cluster&lt;/strong&gt; — A group of EC2 instances or Fargate compute resources on which tasks are scheduled.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ECS Task Definition — Complete Example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"family"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"myapp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"networkMode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"awsvpc"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"requiresCompatibilities"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"FARGATE"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"cpu"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"512"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"memory"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1024"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"executionRoleArn"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:iam::123456789:role/ecsTaskExecutionRole"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"taskRoleArn"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:iam::123456789:role/ecsTaskRole"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"containerDefinitions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"myapp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"image"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"123456789.dkr.ecr.ap-south-1.amazonaws.com/myapp:v1.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"portMappings"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"containerPort"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"protocol"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tcp"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"environment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"NODE_ENV"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"production"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"secrets"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DATABASE_URL"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"valueFrom"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:secretsmanager:ap-south-1:123456789:secret:myapp/db-url"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"logConfiguration"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"logDriver"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"awslogs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"options"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"awslogs-group"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/ecs/myapp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"awslogs-region"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ap-south-1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"awslogs-stream-prefix"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ecs"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"healthCheck"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"CMD-SHELL"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"curl -f http://localhost:3000/health || exit 1"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"interval"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"timeout"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"retries"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"startPeriod"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Never store secrets directly in environment variable definitions. Use AWS Secrets Manager or Parameter Store. ECS automatically injects secrets as environment variables at runtime — they are not stored in the image.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Practical AWS Deployment Workflows
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Complete Deployment Script — React App to AWS Fargate
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Copy this script into your project, set the variables to match your setup, and deploy your entire application with a single command: &lt;code&gt;bash deploy.sh&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="c"&gt;# deploy.sh - Complete deployment script&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;   &lt;span class="c"&gt;# Stop the script if any step fails&lt;/span&gt;

&lt;span class="c"&gt;# Variables — set your own values here&lt;/span&gt;
&lt;span class="nv"&gt;APP_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"myreactapp"&lt;/span&gt;
&lt;span class="nv"&gt;AWS_ACCOUNT_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"123456789012"&lt;/span&gt;
&lt;span class="nv"&gt;AWS_REGION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"ap-south-1"&lt;/span&gt;
&lt;span class="nv"&gt;ECR_REGISTRY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$AWS_ACCOUNT_ID&lt;/span&gt;&lt;span class="s2"&gt;.dkr.ecr.&lt;/span&gt;&lt;span class="nv"&gt;$AWS_REGION&lt;/span&gt;&lt;span class="s2"&gt;.amazonaws.com"&lt;/span&gt;
&lt;span class="nv"&gt;IMAGE_TAG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git rev-parse &lt;span class="nt"&gt;--short&lt;/span&gt; HEAD&lt;span class="si"&gt;)&lt;/span&gt;   &lt;span class="c"&gt;# Use Git SHA as tag&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Starting deployment of &lt;/span&gt;&lt;span class="nv"&gt;$APP_NAME&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;$IMAGE_TAG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# Step 1: Build image&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Building Docker image..."&lt;/span&gt;
docker build &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--build-arg&lt;/span&gt; &lt;span class="nv"&gt;REACT_APP_API_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://api.myapp.com &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-t&lt;/span&gt; &lt;span class="nv"&gt;$APP_NAME&lt;/span&gt;:&lt;span class="nv"&gt;$IMAGE_TAG&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="c"&gt;# Step 2: Authenticate with ECR&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Authenticating with ECR..."&lt;/span&gt;
aws ecr get-login-password &lt;span class="nt"&gt;--region&lt;/span&gt; &lt;span class="nv"&gt;$AWS_REGION&lt;/span&gt; | &lt;span class="se"&gt;\&lt;/span&gt;
  docker login &lt;span class="nt"&gt;--username&lt;/span&gt; AWS &lt;span class="nt"&gt;--password-stdin&lt;/span&gt; &lt;span class="nv"&gt;$ECR_REGISTRY&lt;/span&gt;

&lt;span class="c"&gt;# Step 3: Tag for ECR&lt;/span&gt;
docker tag &lt;span class="nv"&gt;$APP_NAME&lt;/span&gt;:&lt;span class="nv"&gt;$IMAGE_TAG&lt;/span&gt; &lt;span class="nv"&gt;$ECR_REGISTRY&lt;/span&gt;/&lt;span class="nv"&gt;$APP_NAME&lt;/span&gt;:&lt;span class="nv"&gt;$IMAGE_TAG&lt;/span&gt;
docker tag &lt;span class="nv"&gt;$APP_NAME&lt;/span&gt;:&lt;span class="nv"&gt;$IMAGE_TAG&lt;/span&gt; &lt;span class="nv"&gt;$ECR_REGISTRY&lt;/span&gt;/&lt;span class="nv"&gt;$APP_NAME&lt;/span&gt;:latest

&lt;span class="c"&gt;# Step 4: Push to ECR&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Pushing to ECR..."&lt;/span&gt;
docker push &lt;span class="nv"&gt;$ECR_REGISTRY&lt;/span&gt;/&lt;span class="nv"&gt;$APP_NAME&lt;/span&gt;:&lt;span class="nv"&gt;$IMAGE_TAG&lt;/span&gt;
docker push &lt;span class="nv"&gt;$ECR_REGISTRY&lt;/span&gt;/&lt;span class="nv"&gt;$APP_NAME&lt;/span&gt;:latest

&lt;span class="c"&gt;# Step 5: Update ECS service (force new deployment)&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Updating ECS service..."&lt;/span&gt;
aws ecs update-service &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--cluster&lt;/span&gt; production &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--service&lt;/span&gt; &lt;span class="nv"&gt;$APP_NAME&lt;/span&gt;&lt;span class="nt"&gt;-service&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--force-new-deployment&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; &lt;span class="nv"&gt;$AWS_REGION&lt;/span&gt;

&lt;span class="c"&gt;# Step 6: Wait for deployment to stabilize&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Waiting for deployment to stabilize..."&lt;/span&gt;
aws ecs &lt;span class="nb"&gt;wait &lt;/span&gt;services-stable &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--cluster&lt;/span&gt; production &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--services&lt;/span&gt; &lt;span class="nv"&gt;$APP_NAME&lt;/span&gt;&lt;span class="nt"&gt;-service&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; &lt;span class="nv"&gt;$AWS_REGION&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Deployment successful! Image: &lt;/span&gt;&lt;span class="nv"&gt;$ECR_REGISTRY&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$APP_NAME&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;$IMAGE_TAG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  AWS Fargate with Application Load Balancer
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create ECS cluster (Fargate mode)&lt;/span&gt;
aws ecs create-cluster &lt;span class="nt"&gt;--cluster-name&lt;/span&gt; production

&lt;span class="c"&gt;# Create ALB target group&lt;/span&gt;
aws elbv2 create-target-group &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; myapp-tg &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--protocol&lt;/span&gt; HTTP &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--port&lt;/span&gt; 3000 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--vpc-id&lt;/span&gt; vpc-12345 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--target-type&lt;/span&gt; ip &lt;span class="se"&gt;\ &lt;/span&gt;             &lt;span class="c"&gt;# Use 'ip' for Fargate, not 'instance'&lt;/span&gt;
  &lt;span class="nt"&gt;--health-check-path&lt;/span&gt; /health

&lt;span class="c"&gt;# Create ECS service with Fargate — 2 replicas for high availability&lt;/span&gt;
aws ecs create-service &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--cluster&lt;/span&gt; production &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--service-name&lt;/span&gt; myapp-service &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--task-definition&lt;/span&gt; myapp:1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--desired-count&lt;/span&gt; 2 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--launch-type&lt;/span&gt; FARGATE &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--network-configuration&lt;/span&gt; &lt;span class="s2"&gt;"awsvpcConfiguration={subnets=[subnet-1,subnet-2],securityGroups=[sg-123],assignPublicIp=ENABLED}"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--load-balancers&lt;/span&gt; &lt;span class="s2"&gt;"targetGroupArn=arn:aws:elasticloadbalancing:...,containerName=myapp,containerPort=3000"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ECR — Common Operations
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create ECR repository&lt;/span&gt;
aws ecr create-repository &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--repository-name&lt;/span&gt; myapp &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--image-scanning-configuration&lt;/span&gt; &lt;span class="nv"&gt;scanOnPush&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="se"&gt;\ &lt;/span&gt;  &lt;span class="c"&gt;# Enable vulnerability scanning&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; ap-south-1

&lt;span class="c"&gt;# List images in repository&lt;/span&gt;
aws ecr list-images &lt;span class="nt"&gt;--repository-name&lt;/span&gt; myapp &lt;span class="nt"&gt;--region&lt;/span&gt; ap-south-1

&lt;span class="c"&gt;# Delete old images (cost management)&lt;/span&gt;
aws ecr batch-delete-image &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--repository-name&lt;/span&gt; myapp &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--image-ids&lt;/span&gt; &lt;span class="nv"&gt;imageTag&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;old-tag

&lt;span class="c"&gt;# Lifecycle policy — automatically delete old images&lt;/span&gt;
aws ecr put-lifecycle-policy &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--repository-name&lt;/span&gt; myapp &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--lifecycle-policy-text&lt;/span&gt; &lt;span class="s1"&gt;'{
    "rules": [{
      "rulePriority": 1,
      "description": "Keep last 10 images",
      "selection": {
        "tagStatus": "any",
        "countType": "imageCountMoreThan",
        "countNumber": 10
      },
      "action": {"type": "expire"}
    }]
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  10 Hands-On Docker Projects
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Build all of these projects and push them to GitHub. Each project can become a resume bullet point. Recruiters and interviewers want to see working code — that is far more impactful than simply claiming Docker knowledge.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Project 1: React Portfolio Site
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Difficulty:&lt;/strong&gt; Beginner | &lt;strong&gt;Stack:&lt;/strong&gt; React, Nginx, Docker | &lt;strong&gt;Concepts:&lt;/strong&gt; Multi-stage build, static file serving&lt;/p&gt;

&lt;p&gt;Convert a React application into a production-ready Docker image using Nginx for serving and optimal caching.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Stage 1: Build React app&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;node:18-alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;builder&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm ci
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm run build

&lt;span class="c"&gt;# Stage 2: Serve with Nginx&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; nginx:1.25-alpine&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /app/build /usr/share/nginx/html&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; nginx.conf /etc/nginx/conf.d/default.conf&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 80&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["nginx", "-g", "daemon off;"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; portfolio &lt;span class="nb"&gt;.&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:80 &lt;span class="nt"&gt;--name&lt;/span&gt; portfolio portfolio
&lt;span class="c"&gt;# Visit: http://localhost:8080&lt;/span&gt;

&lt;span class="c"&gt;# Check image size&lt;/span&gt;
docker images portfolio
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Resume bullet — &lt;em&gt;"Containerized React portfolio using Docker multi-stage builds with Nginx, achieving 90%+ image size reduction vs non-optimized builds; deployed via CI/CD pipeline."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Project 2: Secure Node.js REST API
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Difficulty:&lt;/strong&gt; Beginner | &lt;strong&gt;Stack:&lt;/strong&gt; Node.js, Express, Docker | &lt;strong&gt;Concepts:&lt;/strong&gt; Non-root user, health checks, production dependencies only&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:18-alpine&lt;/span&gt;

&lt;span class="c"&gt;# Security: create a non-root user&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;addgroup &lt;span class="nt"&gt;-S&lt;/span&gt; appgroup &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; adduser &lt;span class="nt"&gt;-S&lt;/span&gt; appuser &lt;span class="nt"&gt;-G&lt;/span&gt; appgroup

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm ci &lt;span class="nt"&gt;--only&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;production    &lt;span class="c"&gt;# Only production dependencies&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --chown=appuser:appgroup . .&lt;/span&gt;

&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; appuser                    # Switch to non-root user&lt;/span&gt;

&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 3000&lt;/span&gt;

&lt;span class="k"&gt;HEALTHCHECK&lt;/span&gt;&lt;span class="s"&gt; --interval=30s --timeout=5s --start-period=10s \&lt;/span&gt;
  CMD node -e "require('http').get('http://localhost:3000/health', r =&amp;gt; process.exit(r.statusCode === 200 ? 0 : 1))"

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["node", "server.js"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; node-api &lt;span class="nb"&gt;.&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 3000:3000 node-api

&lt;span class="c"&gt;# Check health check status&lt;/span&gt;
docker inspect &lt;span class="nt"&gt;--format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'{{.State.Health.Status}}'&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;docker ps &lt;span class="nt"&gt;-q&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Test the API&lt;/span&gt;
curl http://localhost:3000/health
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Project 3: Python FastAPI Data App
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Difficulty:&lt;/strong&gt; Beginner | &lt;strong&gt;Stack:&lt;/strong&gt; Python, FastAPI, Uvicorn | &lt;strong&gt;Concepts:&lt;/strong&gt; Python containerization, pip caching&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; python:3.11-slim           # slim = smaller than full python image&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="c"&gt;# Copy requirements first for better layer caching&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; requirements.txt .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--no-cache-dir&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;

&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; PYTHONUNBUFFERED=1          # Python logs appear in Docker logs&lt;/span&gt;

&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 8000&lt;/span&gt;

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; fastapi-app &lt;span class="nb"&gt;.&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 8000:8000 fastapi-app

&lt;span class="c"&gt;# Interactive API docs&lt;/span&gt;
&lt;span class="c"&gt;# http://localhost:8000/docs&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Project 4: Full MERN Stack Application
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Difficulty:&lt;/strong&gt; Intermediate | &lt;strong&gt;Stack:&lt;/strong&gt; React, Node.js, MongoDB, Redis | &lt;strong&gt;Concepts:&lt;/strong&gt; Multi-service Compose, service discovery, volumes&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# docker-compose.yml - Full MERN Stack&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;frontend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./frontend&lt;/span&gt;
      &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;REACT_APP_API_URL=http://localhost:5000&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3000:80"&lt;/span&gt;               &lt;span class="c1"&gt;# Production: nginx serving&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;backend&lt;/span&gt;

  &lt;span class="na"&gt;backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./backend&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;5000:5000"&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;MONGODB_URI&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mongodb://mongodb:27017/mern_db&lt;/span&gt;
      &lt;span class="na"&gt;REDIS_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis://redis:6379&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;mongodb&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;redis&lt;/span&gt;

  &lt;span class="na"&gt;mongodb&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mongo:7&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;mongo_data:/data/db&lt;/span&gt;

  &lt;span class="na"&gt;redis&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis:7-alpine&lt;/span&gt;

  &lt;span class="na"&gt;mongo-express&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;              &lt;span class="c1"&gt;# MongoDB GUI — development only&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mongo-express&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8081:8081"&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;ME_CONFIG_MONGODB_SERVER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mongodb&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;mongodb&lt;/span&gt;

&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;mongo_data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Start the complete MERN stack&lt;/span&gt;
docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--build&lt;/span&gt;

&lt;span class="c"&gt;# Check all services&lt;/span&gt;
docker compose ps

&lt;span class="c"&gt;# Follow API logs&lt;/span&gt;
docker compose logs &lt;span class="nt"&gt;-f&lt;/span&gt; backend

&lt;span class="c"&gt;# Access points:&lt;/span&gt;
&lt;span class="c"&gt;# Frontend: http://localhost:3000&lt;/span&gt;
&lt;span class="c"&gt;# Backend API: http://localhost:5000&lt;/span&gt;
&lt;span class="c"&gt;# MongoDB GUI: http://localhost:8081&lt;/span&gt;

&lt;span class="c"&gt;# Stop the stack (data is preserved)&lt;/span&gt;
docker compose down
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Resume bullet — &lt;em&gt;"Containerized full MERN stack using Docker Compose with service discovery, named volumes for data persistence, and Mongo Express GUI for development; reduced environment setup time from 2 hours to 5 minutes."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Project 5: WordPress + MySQL — Classic Stack
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Difficulty:&lt;/strong&gt; Intermediate | &lt;strong&gt;Stack:&lt;/strong&gt; WordPress, MySQL 8 | &lt;strong&gt;Concepts:&lt;/strong&gt; Health checks with conditions, restart policies, named volumes&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;mysql&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mysql:8.0&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;MYSQL_ROOT_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rootpassword&lt;/span&gt;
      &lt;span class="na"&gt;MYSQL_DATABASE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;wordpress&lt;/span&gt;
      &lt;span class="na"&gt;MYSQL_USER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;wordpress&lt;/span&gt;
      &lt;span class="na"&gt;MYSQL_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;wordpresspassword&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;mysql_data:/var/lib/mysql&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
    &lt;span class="na"&gt;healthcheck&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CMD"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mysqladmin"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ping"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-h"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;localhost"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10s&lt;/span&gt;
      &lt;span class="na"&gt;retries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
      &lt;span class="na"&gt;start_period&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;30s&lt;/span&gt;

  &lt;span class="na"&gt;wordpress&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;wordpress:latest&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8080:80"&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;WORDPRESS_DB_HOST&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mysql:3306&lt;/span&gt;
      &lt;span class="na"&gt;WORDPRESS_DB_USER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;wordpress&lt;/span&gt;
      &lt;span class="na"&gt;WORDPRESS_DB_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;wordpresspassword&lt;/span&gt;
      &lt;span class="na"&gt;WORDPRESS_DB_NAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;wordpress&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;wordpress_data:/var/www/html&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;mysql&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;service_healthy&lt;/span&gt;    &lt;span class="c1"&gt;# Wait until MySQL is healthy&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;

&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;mysql_data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;wordpress_data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;span class="c"&gt;# MySQL health check will be satisfied before WordPress starts&lt;/span&gt;
docker compose ps

&lt;span class="c"&gt;# Visit: http://localhost:8080&lt;/span&gt;
&lt;span class="c"&gt;# Complete the WordPress setup wizard&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Project 6: Microservices with Nginx API Gateway
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Difficulty:&lt;/strong&gt; Intermediate | &lt;strong&gt;Stack:&lt;/strong&gt; Node.js microservices, Nginx gateway, PostgreSQL | &lt;strong&gt;Concepts:&lt;/strong&gt; API gateway pattern, service isolation, multiple databases&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# Single entry point — all traffic routes through here&lt;/span&gt;
  &lt;span class="na"&gt;api-gateway&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx:alpine&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;80:80"&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./nginx/gateway.conf:/etc/nginx/conf.d/default.conf&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;user-service&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;product-service&lt;/span&gt;

  &lt;span class="na"&gt;user-service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./user-service&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;DB_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgresql://postgres:password@user-db:5432/users&lt;/span&gt;
    &lt;span class="c1"&gt;# No ports exposed externally — only accessible through the gateway&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;user-db&lt;/span&gt;

  &lt;span class="na"&gt;product-service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./product-service&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;DB_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgresql://postgres:password@product-db:5432/products&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;product-db&lt;/span&gt;

  &lt;span class="c1"&gt;# Each service has its own database&lt;/span&gt;
  &lt;span class="na"&gt;user-db&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres:16-alpine&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_DB&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;users&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;password&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;user_db_data:/var/lib/postgresql/data&lt;/span&gt;

  &lt;span class="na"&gt;product-db&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres:16-alpine&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_DB&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;products&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;password&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;product_db_data:/var/lib/postgresql/data&lt;/span&gt;

&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;user_db_data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;product_db_data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Resume bullet — &lt;em&gt;"Designed and containerized microservices architecture with Nginx API gateway, service isolation, and per-service databases using Docker Compose; demonstrating event-driven service communication patterns."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Project 7: Image Size Optimization Challenge
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Difficulty:&lt;/strong&gt; Intermediate | &lt;strong&gt;Goal:&lt;/strong&gt; 1.2 GB → under 100 MB | &lt;strong&gt;Concepts:&lt;/strong&gt; Multi-stage builds, Alpine, production deps, .dockerignore&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# BEFORE: Naive approach — DO NOT USE in production&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; ubuntu:22.04&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; nodejs npm
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . /app&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;              &lt;span class="c"&gt;# All deps including devDependencies&lt;/span&gt;
&lt;span class="c"&gt;# Final image size: ~1.2 GB&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# AFTER: Optimized multi-stage build&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;node:18-alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;builder&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm ci                   &lt;span class="c"&gt;# All deps for building&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm run build            &lt;span class="c"&gt;# Compile/transpile&lt;/span&gt;

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:18-alpine          # Fresh slim base&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm ci &lt;span class="nt"&gt;--only&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;production &lt;span class="c"&gt;# Production deps only&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /app/dist ./dist&lt;/span&gt;
&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; node                    # Non-root&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["node", "dist/server.js"]&lt;/span&gt;
&lt;span class="c"&gt;# Final image size: ~85 MB (93% reduction)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Compare before and after sizes&lt;/span&gt;
docker build &lt;span class="nt"&gt;-f&lt;/span&gt; Dockerfile.before &lt;span class="nt"&gt;-t&lt;/span&gt; app:before &lt;span class="nb"&gt;.&lt;/span&gt;
docker build &lt;span class="nt"&gt;-f&lt;/span&gt; Dockerfile.after &lt;span class="nt"&gt;-t&lt;/span&gt; app:after &lt;span class="nb"&gt;.&lt;/span&gt;
docker images | &lt;span class="nb"&gt;grep &lt;/span&gt;app

&lt;span class="c"&gt;# Detailed layer analysis&lt;/span&gt;
docker &lt;span class="nb"&gt;history &lt;/span&gt;app:after

&lt;span class="c"&gt;# Layer-by-layer analysis with dive&lt;/span&gt;
docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; /var/run/docker.sock:/var/run/docker.sock &lt;span class="se"&gt;\&lt;/span&gt;
  wagoodman/dive app:after
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Project 8: Full Monitoring Stack
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Difficulty:&lt;/strong&gt; Advanced | &lt;strong&gt;Stack:&lt;/strong&gt; App + Prometheus + Grafana + cAdvisor | &lt;strong&gt;Concepts:&lt;/strong&gt; Metrics collection, dashboards, alerting&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3000:3000"&lt;/span&gt;
    &lt;span class="c1"&gt;# App must expose a /metrics endpoint (use prom-client npm package)&lt;/span&gt;

  &lt;span class="na"&gt;prometheus&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;prom/prometheus&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;9090:9090"&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./prometheus.yml:/etc/prometheus/prometheus.yml&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;prometheus_data:/prometheus&lt;/span&gt;

  &lt;span class="na"&gt;grafana&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;grafana/grafana&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3001:3000"&lt;/span&gt;            &lt;span class="c1"&gt;# Use 3001 to avoid conflict with the app&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;grafana_data:/var/lib/grafana&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;prometheus&lt;/span&gt;

  &lt;span class="na"&gt;cadvisor&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gcr.io/cadvisor/cadvisor&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/:/rootfs:ro&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/var/run:/var/run:ro&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/sys:/sys:ro&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/var/lib/docker:/var/lib/docker:ro&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8080:8080"&lt;/span&gt;

&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;prometheus_data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;grafana_data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt;

&lt;span class="c"&gt;# Access points:&lt;/span&gt;
&lt;span class="c"&gt;# App: http://localhost:3000&lt;/span&gt;
&lt;span class="c"&gt;# Prometheus: http://localhost:9090&lt;/span&gt;
&lt;span class="c"&gt;# Grafana: http://localhost:3001 (admin/admin)&lt;/span&gt;
&lt;span class="c"&gt;# cAdvisor: http://localhost:8080&lt;/span&gt;

&lt;span class="c"&gt;# In Grafana:&lt;/span&gt;
&lt;span class="c"&gt;# 1. Add data source: Prometheus, URL = http://prometheus:9090&lt;/span&gt;
&lt;span class="c"&gt;# 2. Import dashboard: ID 893 (Docker monitoring)&lt;/span&gt;
&lt;span class="c"&gt;# 3. Watch container metrics in real time&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Project 9: Complete CI/CD Pipeline
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Difficulty:&lt;/strong&gt; Advanced | &lt;strong&gt;Stack:&lt;/strong&gt; GitHub Actions, Docker Hub, SSH Deploy | &lt;strong&gt;Concepts:&lt;/strong&gt; Automated build, test, push, deploy&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CI/CD Pipeline&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;v*'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run tests in Docker&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;docker build --target test -t test-image .&lt;/span&gt;
          &lt;span class="s"&gt;docker run --rm test-image npm test&lt;/span&gt;

  &lt;span class="na"&gt;build-push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/setup-buildx-action@v3&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/login-action@v3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.DOCKERHUB_USERNAME }}&lt;/span&gt;
          &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.DOCKERHUB_TOKEN }}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/build-push-action@v5&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
          &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;${{ secrets.DOCKERHUB_USERNAME }}/myapp:latest&lt;/span&gt;
            &lt;span class="s"&gt;${{ secrets.DOCKERHUB_USERNAME }}/myapp:${{ github.sha }}&lt;/span&gt;
          &lt;span class="na"&gt;cache-from&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;type=gha&lt;/span&gt;
          &lt;span class="na"&gt;cache-to&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;type=gha,mode=max&lt;/span&gt;

  &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;build-push&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy to server via SSH&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;appleboy/ssh-action@v1.0.0&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.SERVER_HOST }}&lt;/span&gt;
          &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.SSH_KEY }}&lt;/span&gt;
          &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;docker pull ${{ secrets.DOCKERHUB_USERNAME }}/myapp:latest&lt;/span&gt;
            &lt;span class="s"&gt;docker stop myapp || true&lt;/span&gt;
            &lt;span class="s"&gt;docker rm myapp || true&lt;/span&gt;
            &lt;span class="s"&gt;docker run -d \&lt;/span&gt;
              &lt;span class="s"&gt;--name myapp \&lt;/span&gt;
              &lt;span class="s"&gt;--restart unless-stopped \&lt;/span&gt;
              &lt;span class="s"&gt;-p 80:3000 \&lt;/span&gt;
              &lt;span class="s"&gt;${{ secrets.DOCKERHUB_USERNAME }}/myapp:latest&lt;/span&gt;
            &lt;span class="s"&gt;echo "Deployment complete!"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Resume bullet — &lt;em&gt;"Implemented end-to-end CI/CD pipeline using GitHub Actions: automated Docker image build and test on every push, Docker Hub publish with semantic versioning, and zero-touch SSH deployment to production server."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Project 10: Full Observability Stack (ELK + Jaeger + Prometheus)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Difficulty:&lt;/strong&gt; Advanced | &lt;strong&gt;Stack:&lt;/strong&gt; OpenTelemetry, Jaeger, Prometheus, ELK Stack | &lt;strong&gt;Concepts:&lt;/strong&gt; Metrics + Tracing + Logging — the three pillars of observability&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# Your application&lt;/span&gt;
  &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;OTEL_SERVICE_NAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myapp&lt;/span&gt;
      &lt;span class="na"&gt;OTEL_EXPORTER_OTLP_ENDPOINT&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http://otel-collector:4318&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;otel-collector&lt;/span&gt;

  &lt;span class="c1"&gt;# OpenTelemetry Collector — central data routing hub&lt;/span&gt;
  &lt;span class="na"&gt;otel-collector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;otel/opentelemetry-collector-contrib&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./otel-config.yml:/etc/otelcol-contrib/config.yaml&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;4318:4318"&lt;/span&gt;   &lt;span class="c1"&gt;# OTLP HTTP&lt;/span&gt;

  &lt;span class="c1"&gt;# Distributed tracing — request flows across services&lt;/span&gt;
  &lt;span class="na"&gt;jaeger&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;jaegertracing/all-in-one:latest&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;16686:16686"&lt;/span&gt;   &lt;span class="c1"&gt;# Jaeger UI&lt;/span&gt;

  &lt;span class="c1"&gt;# Metrics — numeric time-series data&lt;/span&gt;
  &lt;span class="na"&gt;prometheus&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;prom/prometheus&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;9090:9090"&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./prometheus.yml:/etc/prometheus/prometheus.yml&lt;/span&gt;

  &lt;span class="c1"&gt;# Dashboards — metrics visualization&lt;/span&gt;
  &lt;span class="na"&gt;grafana&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;grafana/grafana&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3001:3000"&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;GF_SECURITY_ADMIN_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;admin123&lt;/span&gt;

  &lt;span class="c1"&gt;# Logs — structured log storage&lt;/span&gt;
  &lt;span class="na"&gt;elasticsearch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;elasticsearch:8.11.0&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;discovery.type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;single-node&lt;/span&gt;
      &lt;span class="na"&gt;xpack.security.enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;false"&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;es_data:/usr/share/elasticsearch/data&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;9200:9200"&lt;/span&gt;

  &lt;span class="c1"&gt;# Log visualization&lt;/span&gt;
  &lt;span class="na"&gt;kibana&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kibana:8.11.0&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;5601:5601"&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;ELASTICSEARCH_HOSTS&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http://elasticsearch:9200&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;elasticsearch&lt;/span&gt;

&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;es_data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Start full observability stack&lt;/span&gt;
docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt;

&lt;span class="c"&gt;# Wait for Elasticsearch to finish starting&lt;/span&gt;
docker compose logs &lt;span class="nt"&gt;-f&lt;/span&gt; elasticsearch | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"started"&lt;/span&gt;

&lt;span class="c"&gt;# Access points:&lt;/span&gt;
&lt;span class="c"&gt;# App:          http://localhost:3000&lt;/span&gt;
&lt;span class="c"&gt;# Jaeger UI:    http://localhost:16686  (request traces)&lt;/span&gt;
&lt;span class="c"&gt;# Prometheus:   http://localhost:9090   (raw metrics)&lt;/span&gt;
&lt;span class="c"&gt;# Grafana:      http://localhost:3001   (metric dashboards)&lt;/span&gt;
&lt;span class="c"&gt;# Kibana:       http://localhost:5601   (log analysis)&lt;/span&gt;

&lt;span class="c"&gt;# Generate some traffic&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;i &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;1..20&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do &lt;/span&gt;curl http://localhost:3000/api/users&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Resume bullet — &lt;em&gt;"Implemented complete observability stack using Docker Compose: OpenTelemetry instrumentation, Jaeger distributed tracing, Prometheus + Grafana metrics dashboards, and ELK stack log aggregation — full visibility into system behavior."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Projects Summary — Difficulty Progression
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Project&lt;/th&gt;
&lt;th&gt;Difficulty&lt;/th&gt;
&lt;th&gt;Key Skill&lt;/th&gt;
&lt;th&gt;Resume Impact&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;React Portfolio&lt;/td&gt;
&lt;td&gt;Beginner&lt;/td&gt;
&lt;td&gt;Multi-stage build&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Node.js API&lt;/td&gt;
&lt;td&gt;Beginner&lt;/td&gt;
&lt;td&gt;Security, health checks&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Python FastAPI&lt;/td&gt;
&lt;td&gt;Beginner&lt;/td&gt;
&lt;td&gt;Python containerization&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;MERN Stack&lt;/td&gt;
&lt;td&gt;Intermediate&lt;/td&gt;
&lt;td&gt;Multi-service Compose&lt;/td&gt;
&lt;td&gt;Very High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;WordPress + MySQL&lt;/td&gt;
&lt;td&gt;Intermediate&lt;/td&gt;
&lt;td&gt;Health check conditions&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Microservices&lt;/td&gt;
&lt;td&gt;Intermediate&lt;/td&gt;
&lt;td&gt;API gateway pattern&lt;/td&gt;
&lt;td&gt;Very High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;Size Optimization&lt;/td&gt;
&lt;td&gt;Intermediate&lt;/td&gt;
&lt;td&gt;Layer optimization&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;Monitoring Stack&lt;/td&gt;
&lt;td&gt;Advanced&lt;/td&gt;
&lt;td&gt;Prometheus + Grafana&lt;/td&gt;
&lt;td&gt;Very High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;CI/CD Pipeline&lt;/td&gt;
&lt;td&gt;Advanced&lt;/td&gt;
&lt;td&gt;GitHub Actions&lt;/td&gt;
&lt;td&gt;Very High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;Observability Stack&lt;/td&gt;
&lt;td&gt;Advanced&lt;/td&gt;
&lt;td&gt;OTel + ELK + Jaeger&lt;/td&gt;
&lt;td&gt;Exceptional&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Common Errors and Solutions
&lt;/h2&gt;

&lt;p&gt;The following errors are encountered by almost every Docker developer at some point. Each includes the root cause, solution, and prevention guidance.&lt;/p&gt;

&lt;h3&gt;
  
  
  Error: Cannot connect to the Docker daemon
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error: Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Root Cause:&lt;/strong&gt; The Docker daemon (dockerd) has not started or has crashed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check daemon status&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl status docker

&lt;span class="c"&gt;# Start the daemon&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl start docker

&lt;span class="c"&gt;# Enable on boot&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;docker

&lt;span class="c"&gt;# Verify&lt;/span&gt;
docker info
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Prevention:&lt;/strong&gt; Always enable the Docker service on boot with &lt;code&gt;systemctl enable docker&lt;/code&gt;. On Mac, ensure Docker Desktop is running before using the CLI.&lt;/p&gt;

&lt;h3&gt;
  
  
  Error: Permission denied on Docker socket
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Root Cause:&lt;/strong&gt; The current user is not a member of the &lt;code&gt;docker&lt;/code&gt; group.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Add user to docker group&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;usermod &lt;span class="nt"&gt;-aG&lt;/span&gt; docker &lt;span class="nv"&gt;$USER&lt;/span&gt;

&lt;span class="c"&gt;# Apply group change immediately (or log out and back in)&lt;/span&gt;
newgrp docker

&lt;span class="c"&gt;# Verify&lt;/span&gt;
docker run hello-world
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Prevention:&lt;/strong&gt; Add the user to the docker group during initial Docker installation. In CI/CD pipelines, add the runner user to the docker group.&lt;/p&gt;

&lt;h3&gt;
  
  
  Error: Port is already allocated
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker: Error response from daemon: driver failed programming external connectivity on endpoint myapp: Bind for 0.0.0.0:8080 failed: port is already allocated.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Root Cause:&lt;/strong&gt; Host port 8080 is already in use by another process or container.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Find what is using the port&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;lsof &lt;span class="nt"&gt;-i&lt;/span&gt; :8080
&lt;span class="nb"&gt;sudo &lt;/span&gt;ss &lt;span class="nt"&gt;-tulpn&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; :8080

&lt;span class="c"&gt;# Kill the process (replace PID)&lt;/span&gt;
&lt;span class="nb"&gt;kill&lt;/span&gt; &lt;span class="nt"&gt;-9&lt;/span&gt; &amp;lt;PID&amp;gt;

&lt;span class="c"&gt;# OR use a different host port&lt;/span&gt;
docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 8081:80 nginx

&lt;span class="c"&gt;# Find running containers using the port&lt;/span&gt;
docker ps | &lt;span class="nb"&gt;grep &lt;/span&gt;8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Prevention:&lt;/strong&gt; Use environment variables for port configuration. Run &lt;code&gt;docker ps&lt;/code&gt; before starting new containers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Error: No space left on device
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ERROR: failed to solve: ... write /var/lib/docker/...: no space left on device
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Root Cause:&lt;/strong&gt; The Docker daemon's disk space is exhausted — old images, containers, and volumes have consumed all available space.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check disk usage&lt;/span&gt;
&lt;span class="nb"&gt;df&lt;/span&gt; &lt;span class="nt"&gt;-h&lt;/span&gt;
docker system &lt;span class="nb"&gt;df&lt;/span&gt;

&lt;span class="c"&gt;# Full cleanup (removes everything unused)&lt;/span&gt;
docker system prune &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nt"&gt;--volumes&lt;/span&gt;

&lt;span class="c"&gt;# Selective cleanup&lt;/span&gt;
docker image prune &lt;span class="nt"&gt;-a&lt;/span&gt;    &lt;span class="c"&gt;# Remove unused images&lt;/span&gt;
docker container prune   &lt;span class="c"&gt;# Remove stopped containers&lt;/span&gt;
docker volume prune      &lt;span class="c"&gt;# Remove unused volumes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Prevention:&lt;/strong&gt; Set up a cron job for &lt;code&gt;docker system prune&lt;/code&gt;. Monitor &lt;code&gt;/var/lib/docker&lt;/code&gt; size. Use multi-stage builds to keep images small.&lt;/p&gt;

&lt;h3&gt;
  
  
  Error: Container exits immediately
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Root Cause:&lt;/strong&gt; The container's main process (CMD/ENTRYPOINT) completed, or it crashed with an error.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check logs first&lt;/span&gt;
docker logs container_id
docker logs container_id &lt;span class="nt"&gt;--tail&lt;/span&gt; 50

&lt;span class="c"&gt;# Check exit code&lt;/span&gt;
docker inspect &lt;span class="nt"&gt;--format&lt;/span&gt; &lt;span class="s1"&gt;'{{.State.ExitCode}}'&lt;/span&gt; container_id

&lt;span class="c"&gt;# Run interactively to debug&lt;/span&gt;
docker run &lt;span class="nt"&gt;-it&lt;/span&gt; myapp bash

&lt;span class="c"&gt;# Bad: CMD nginx (exits immediately — starts as daemon)&lt;/span&gt;
&lt;span class="c"&gt;# Good: CMD ["nginx", "-g", "daemon off;"]&lt;/span&gt;

&lt;span class="c"&gt;# Override entrypoint for debugging&lt;/span&gt;
docker run &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;--entrypoint&lt;/span&gt; bash myapp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Prevention:&lt;/strong&gt; Use foreground commands in CMD. Test CMD locally first. Add proper health checks.&lt;/p&gt;

&lt;h3&gt;
  
  
  Error: Pull access denied
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error response from daemon: pull access denied for myapp, repository does not exist or may require 'docker login'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Root Cause:&lt;/strong&gt; The image name is incorrect, authentication is missing for a private registry, or the image does not exist.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Login to Docker Hub&lt;/span&gt;
docker login

&lt;span class="c"&gt;# Login to a private registry&lt;/span&gt;
docker login registry.mycompany.com &lt;span class="nt"&gt;-u&lt;/span&gt; username &lt;span class="nt"&gt;-p&lt;/span&gt; password

&lt;span class="c"&gt;# Check exact image name and tag&lt;/span&gt;
docker pull myapp:latest

&lt;span class="c"&gt;# Full path required for private registries&lt;/span&gt;
docker pull registry.mycompany.com/team/myapp:v1.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Prevention:&lt;/strong&gt; Use full image paths. Store credentials in Docker credential stores, not plain text. Set up registry mirrors for CI/CD.&lt;/p&gt;

&lt;h3&gt;
  
  
  Error: Build context is extremely large
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Sending build context to Docker daemon  2.5GB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Root Cause:&lt;/strong&gt; A &lt;code&gt;.dockerignore&lt;/code&gt; file is missing, so Docker sends the entire project directory (including &lt;code&gt;node_modules&lt;/code&gt;, &lt;code&gt;.git&lt;/code&gt;, etc.) to the daemon.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create .dockerignore file&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; .dockerignore &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
node_modules/
dist/
build/
.git/
.env
.env.local
*.log
npm-debug.log*
.DS_Store
coverage/
.nyc_output
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="c"&gt;# Verify build context size after&lt;/span&gt;
docker build &lt;span class="nt"&gt;--no-cache&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt; 2&amp;gt;&amp;amp;1 | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Prevention:&lt;/strong&gt; Always create a &lt;code&gt;.dockerignore&lt;/code&gt; when starting a new Docker project. Commit it to version control alongside your code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Error: Docker Hub rate limit exceeded
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error response from daemon: toomanyrequests: You have reached your pull rate limit.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Root Cause:&lt;/strong&gt; Docker Hub anonymous pull limit has been hit (100 pulls per 6 hours unauthenticated, 200 for free accounts).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Authenticate first to increase the limit&lt;/span&gt;
docker login
docker pull nginx

&lt;span class="c"&gt;# OR configure a registry mirror in /etc/docker/daemon.json:&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"registry-mirrors"&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"https://mirror.gcr.io"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# Restart Docker after configuration change&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl restart docker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Prevention:&lt;/strong&gt; Always authenticate in CI/CD. Use ECR or GCR as a pull-through cache.&lt;/p&gt;

&lt;h3&gt;
  
  
  Error: Container OOMKilled — exit code 137
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Root Cause:&lt;/strong&gt; The container exceeded its memory limit. The Linux OOM Killer forcefully terminated the process. Exit code 137 = 128 + 9 (SIGKILL).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check if OOM killed&lt;/span&gt;
docker inspect myapp | jq &lt;span class="s1"&gt;'.[0].State.OOMKilled'&lt;/span&gt;

&lt;span class="c"&gt;# Check kernel logs for OOM&lt;/span&gt;
dmesg | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"oom&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;killed process"&lt;/span&gt;

&lt;span class="c"&gt;# Increase memory limit&lt;/span&gt;
docker run &lt;span class="nt"&gt;--memory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1g &lt;span class="nt"&gt;--memory-swap&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2g myapp

&lt;span class="c"&gt;# Monitor memory usage&lt;/span&gt;
docker stats myapp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Prevention:&lt;/strong&gt; Profile application memory in development. Set realistic limits based on actual usage plus a 20% buffer. Add memory monitoring alerts in production.&lt;/p&gt;

&lt;h3&gt;
  
  
  Error: ECONNREFUSED connecting to database
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error: connect ECONNREFUSED 127.0.0.1:5432
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Root Cause:&lt;/strong&gt; The application inside the container is using &lt;code&gt;127.0.0.1&lt;/code&gt; or &lt;code&gt;localhost&lt;/code&gt; to connect to the database. Inside a container, &lt;code&gt;localhost&lt;/code&gt; refers to the container itself — not another container.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# WRONG - localhost inside container = the container itself&lt;/span&gt;
&lt;span class="nv"&gt;DB_HOST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;localhost  &lt;span class="c"&gt;# ❌&lt;/span&gt;
&lt;span class="nv"&gt;DB_HOST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;127.0.0.1  &lt;span class="c"&gt;# ❌&lt;/span&gt;

&lt;span class="c"&gt;# CORRECT - use the container name or service name&lt;/span&gt;
&lt;span class="nv"&gt;DB_HOST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;postgres   &lt;span class="c"&gt;# ✅ (with docker compose)&lt;/span&gt;
&lt;span class="nv"&gt;DB_HOST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;db         &lt;span class="c"&gt;# ✅ (compose service name)&lt;/span&gt;

&lt;span class="c"&gt;# Verify connectivity&lt;/span&gt;
docker &lt;span class="nb"&gt;exec &lt;/span&gt;myapp ping postgres
docker &lt;span class="nb"&gt;exec &lt;/span&gt;myapp nslookup postgres
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Prevention:&lt;/strong&gt; Never hardcode localhost for inter-container communication. Use environment variables for hostnames. Use user-defined networks for DNS resolution.&lt;/p&gt;

&lt;h3&gt;
  
  
  Error: Volume mount permission denied
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error: Permission denied: '/app/data'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Root Cause:&lt;/strong&gt; The container process user does not own the host volume directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Option 1: Match user/group&lt;/span&gt;
docker run &lt;span class="nt"&gt;-u&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;:&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;/data:/app/data myapp

&lt;span class="c"&gt;# Option 2: Fix host directory permissions&lt;/span&gt;
&lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; 1000:1000 ./data

&lt;span class="c"&gt;# Option 3: Fix permissions in Dockerfile&lt;/span&gt;
RUN &lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /app/data &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; node:node /app/data
USER node

&lt;span class="c"&gt;# Option 4: Use named volume (Docker manages permissions)&lt;/span&gt;
docker run &lt;span class="nt"&gt;-v&lt;/span&gt; myapp_data:/app/data myapp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Error: DNS resolution failure between containers
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error: getaddrinfo ENOTFOUND api-service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Root Cause:&lt;/strong&gt; The default bridge network does not provide automatic DNS resolution between containers. Only user-defined networks include the embedded DNS server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create a user-defined network&lt;/span&gt;
docker network create app-network

&lt;span class="c"&gt;# Run both containers on the same network&lt;/span&gt;
docker run &lt;span class="nt"&gt;--name&lt;/span&gt; api-service &lt;span class="nt"&gt;--network&lt;/span&gt; app-network myapi
docker run &lt;span class="nt"&gt;--name&lt;/span&gt; frontend &lt;span class="nt"&gt;--network&lt;/span&gt; app-network myfrontend

&lt;span class="c"&gt;# Now 'api-service' is resolvable by name&lt;/span&gt;
docker &lt;span class="nb"&gt;exec &lt;/span&gt;frontend curl http://api-service:3000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Prevention:&lt;/strong&gt; Always use user-defined networks for multi-container applications. Docker Compose sets this up automatically.&lt;/p&gt;

&lt;h3&gt;
  
  
  Error: COPY file not found in build context
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;COPY failed: file not found in build context or excluded by .dockerignore: stat package.json: file not found
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Root Cause:&lt;/strong&gt; The file does not exist in the build context, &lt;code&gt;.dockerignore&lt;/code&gt; has excluded it, or the Dockerfile is being run from the wrong directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check if file exists&lt;/span&gt;
&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt; package.json

&lt;span class="c"&gt;# Check .dockerignore isn't excluding it&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; .dockerignore | &lt;span class="nb"&gt;grep &lt;/span&gt;package

&lt;span class="c"&gt;# Build from the correct directory&lt;/span&gt;
docker build &lt;span class="nt"&gt;-t&lt;/span&gt; myapp &lt;span class="nb"&gt;.&lt;/span&gt;   &lt;span class="c"&gt;# note the dot — build context = current directory&lt;/span&gt;

&lt;span class="c"&gt;# To build from a parent directory&lt;/span&gt;
docker build &lt;span class="nt"&gt;-t&lt;/span&gt; myapp &lt;span class="nt"&gt;-f&lt;/span&gt; ./subdir/Dockerfile &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Error: exec format error
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;exec /docker-entrypoint.sh: exec format error
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Root Cause:&lt;/strong&gt; The image was built for a different CPU architecture (e.g., built on an M1 Mac for &lt;code&gt;arm64&lt;/code&gt;, then run on a &lt;code&gt;linux/amd64&lt;/code&gt; server).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check image platform&lt;/span&gt;
docker inspect myapp | jq &lt;span class="s1"&gt;'.[0].Architecture'&lt;/span&gt;

&lt;span class="c"&gt;# Build for a specific platform&lt;/span&gt;
docker build &lt;span class="nt"&gt;--platform&lt;/span&gt; linux/amd64 &lt;span class="nt"&gt;-t&lt;/span&gt; myapp &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="c"&gt;# Build a multi-platform image using buildx&lt;/span&gt;
docker buildx create &lt;span class="nt"&gt;--use&lt;/span&gt;
docker buildx build &lt;span class="nt"&gt;--platform&lt;/span&gt; linux/amd64,linux/arm64 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-t&lt;/span&gt; myapp:latest &lt;span class="nt"&gt;--push&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Prevention:&lt;/strong&gt; Always use &lt;code&gt;--platform linux/amd64&lt;/code&gt; when building for production servers from an M1/M2 Mac.&lt;/p&gt;

&lt;h3&gt;
  
  
  Error: Cache keeps invalidating — npm install runs every time
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Root Cause:&lt;/strong&gt; &lt;code&gt;COPY . .&lt;/code&gt; is placed before &lt;code&gt;RUN npm install&lt;/code&gt;, so any file change invalidates the cache and forces &lt;code&gt;npm install&lt;/code&gt; to re-run.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# WRONG - cache breaks on every file change&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:18&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .          # ❌ Any change = cache miss&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;   &lt;span class="c"&gt;# Re-runs every time&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["node", "server.js"]&lt;/span&gt;

&lt;span class="c"&gt;# CORRECT - npm install only re-runs when package.json changes&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:18&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./      # ✅ Only copy dependency manifest&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm ci                 &lt;span class="c"&gt;# Only runs when package.json changes&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .                   # Source code (frequently changing)&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["node", "server.js"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Prevention:&lt;/strong&gt; Always separate dependency installation from source code copying. This is one of the most important Dockerfile optimizations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Error: No configuration file provided (Docker Compose)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;no configuration file provided: not found
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Root Cause:&lt;/strong&gt; Docker Compose cannot find a &lt;code&gt;docker-compose.yml&lt;/code&gt; or &lt;code&gt;compose.yml&lt;/code&gt; in the current directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check the current directory&lt;/span&gt;
&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; compose

&lt;span class="c"&gt;# Navigate to the correct directory and run again&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; /path/to/your/project
docker compose up

&lt;span class="c"&gt;# OR specify the file explicitly&lt;/span&gt;
docker compose &lt;span class="nt"&gt;-f&lt;/span&gt; /path/to/docker-compose.yml up

&lt;span class="c"&gt;# Compose searches for these files in order:&lt;/span&gt;
&lt;span class="c"&gt;# 1. compose.yaml&lt;/span&gt;
&lt;span class="c"&gt;# 2. compose.yml&lt;/span&gt;
&lt;span class="c"&gt;# 3. docker-compose.yaml&lt;/span&gt;
&lt;span class="c"&gt;# 4. docker-compose.yml&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Error: Container status is unhealthy
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Root Cause:&lt;/strong&gt; The &lt;code&gt;HEALTHCHECK&lt;/code&gt; command is failing. The application has not properly started, or the health endpoint is not responding.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Inspect health check output&lt;/span&gt;
docker inspect &lt;span class="nt"&gt;--format&lt;/span&gt; &lt;span class="s1"&gt;'{{json .State.Health}}'&lt;/span&gt; myapp | jq &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="c"&gt;# See the last 5 health check results&lt;/span&gt;
docker inspect myapp | jq &lt;span class="s1"&gt;'.[0].State.Health.Log'&lt;/span&gt;

&lt;span class="c"&gt;# Manually run the health check command&lt;/span&gt;
docker &lt;span class="nb"&gt;exec &lt;/span&gt;myapp curl &lt;span class="nt"&gt;-f&lt;/span&gt; http://localhost:3000/health

&lt;span class="c"&gt;# Temporarily disable to debug&lt;/span&gt;
docker run &lt;span class="nt"&gt;--no-healthcheck&lt;/span&gt; myapp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Prevention:&lt;/strong&gt; Test health check commands manually before adding them to the Dockerfile. Use &lt;code&gt;--start-period&lt;/code&gt; for applications that take time to start. Implement a &lt;code&gt;/health&lt;/code&gt; endpoint in your application.&lt;/p&gt;

&lt;h3&gt;
  
  
  Error: OOMKilled on Python — ModuleNotFoundError
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ModuleNotFoundError: No module named 'flask'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Root Cause:&lt;/strong&gt; &lt;code&gt;requirements.txt&lt;/code&gt; was not copied correctly, or &lt;code&gt;pip install&lt;/code&gt; ran in the wrong directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Correct Dockerfile order&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; python:3.11-slim&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app                           # Set WORKDIR first&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; requirements.txt .                # Copy requirements&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--no-cache-dir&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt  &lt;span class="c"&gt;# Install&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .                               # Then copy code&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["python", "app.py"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Debug: check if packages are installed&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; myapp pip list | &lt;span class="nb"&gt;grep &lt;/span&gt;flask

&lt;span class="c"&gt;# Rebuild without cache&lt;/span&gt;
docker build &lt;span class="nt"&gt;--no-cache&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; myapp &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Error: nginx bind permission denied on port 80
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nginx: [emerg] bind() to 0.0.0.0:80 failed (13: Permission denied)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Root Cause:&lt;/strong&gt; A non-root user cannot bind to privileged ports below 1024.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Option 1: Use port 8080 (non-privileged)&lt;/span&gt;
&lt;span class="c"&gt;# In nginx.conf: listen 8080;&lt;/span&gt;
docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 80:8080 nginx-custom

&lt;span class="c"&gt;# Option 2: Add NET_BIND_SERVICE capability&lt;/span&gt;
docker run &lt;span class="nt"&gt;--cap-add&lt;/span&gt; NET_BIND_SERVICE &lt;span class="nt"&gt;-p&lt;/span&gt; 80:80 nginx-custom

&lt;span class="c"&gt;# Option 3: Use the nginx-unprivileged image (uses port 8080 by default)&lt;/span&gt;
FROM nginxinc/nginx-unprivileged
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; When using a non-root user with nginx, always configure &lt;code&gt;nginx.conf&lt;/code&gt; to listen on port 8080 or higher. Use &lt;code&gt;nginxinc/nginx-unprivileged&lt;/code&gt; as the base image for a secure, non-root nginx setup.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; &lt;code&gt;docker logs&lt;/code&gt; and &lt;code&gt;docker inspect&lt;/code&gt; are your best debugging tools. 90% of Docker errors can be diagnosed with just these two commands.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Debugging Methodology
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Systematic Debugging — Step by Step
&lt;/h3&gt;

&lt;p&gt;When something stops working, follow this systematic checklist. Do not make random changes without first diagnosing the problem.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Approach Docker debugging like a doctor: check symptoms first, then diagnose, then treat. Never make changes without a clear diagnosis.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Step 1: Is the Container Running?
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Show running containers&lt;/span&gt;
docker ps

&lt;span class="c"&gt;# Show ALL containers (including stopped)&lt;/span&gt;
docker ps &lt;span class="nt"&gt;-a&lt;/span&gt;

&lt;span class="c"&gt;# Pretty format with useful columns&lt;/span&gt;
docker ps &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nt"&gt;--format&lt;/span&gt; &lt;span class="s2"&gt;"table {{.Names}}&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s2"&gt;{{.Status}}&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s2"&gt;{{.Ports}}"&lt;/span&gt;

&lt;span class="c"&gt;# Filter by container name&lt;/span&gt;
docker ps &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nt"&gt;--filter&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;myapp
&lt;span class="c"&gt;# Check the STATUS column: "Exited (1) 2 minutes ago" = crashed&lt;/span&gt;
&lt;span class="c"&gt;# "Exited (0) 2 minutes ago" = completed normally&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: What Do the Logs Say?
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Last 100 lines of logs&lt;/span&gt;
docker logs &lt;span class="nt"&gt;--tail&lt;/span&gt; 100 myapp

&lt;span class="c"&gt;# Follow logs in real time&lt;/span&gt;
docker logs &lt;span class="nt"&gt;-f&lt;/span&gt; myapp

&lt;span class="c"&gt;# Logs from the last 1 hour&lt;/span&gt;
docker logs &lt;span class="nt"&gt;--since&lt;/span&gt; 1h myapp

&lt;span class="c"&gt;# Logs with timestamps&lt;/span&gt;
docker logs &lt;span class="nt"&gt;-t&lt;/span&gt; myapp

&lt;span class="c"&gt;# Filter for errors&lt;/span&gt;
docker logs myapp 2&amp;gt;&amp;amp;1 | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"error&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;fatal&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;critical"&lt;/span&gt;

&lt;span class="c"&gt;# Logs persist even for stopped containers until the container is removed&lt;/span&gt;
docker logs myapp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: What Is the Exit Code?
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Get exit code&lt;/span&gt;
docker inspect &lt;span class="nt"&gt;--format&lt;/span&gt; &lt;span class="s1"&gt;'{{.State.ExitCode}}'&lt;/span&gt; myapp

&lt;span class="c"&gt;# Get full state information&lt;/span&gt;
docker inspect &lt;span class="nt"&gt;--format&lt;/span&gt; &lt;span class="s1"&gt;'{{json .State}}'&lt;/span&gt; myapp | jq &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="c"&gt;# Exit code reference:&lt;/span&gt;
&lt;span class="c"&gt;# 0   = Normal exit (success, or CMD completed)&lt;/span&gt;
&lt;span class="c"&gt;# 1   = General error (application error)&lt;/span&gt;
&lt;span class="c"&gt;# 2   = Misuse of shell command&lt;/span&gt;
&lt;span class="c"&gt;# 125 = Docker daemon error&lt;/span&gt;
&lt;span class="c"&gt;# 126 = Container command cannot be invoked&lt;/span&gt;
&lt;span class="c"&gt;# 127 = Container command not found&lt;/span&gt;
&lt;span class="c"&gt;# 130 = Container terminated by Ctrl+C (SIGINT)&lt;/span&gt;
&lt;span class="c"&gt;# 137 = SIGKILL (OOM kill or docker kill)&lt;/span&gt;
&lt;span class="c"&gt;# 139 = Segmentation fault&lt;/span&gt;
&lt;span class="c"&gt;# 143 = SIGTERM (graceful shutdown)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4: Can You Enter the Container?
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Enter a running container (bash for ubuntu/debian)&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; myapp bash

&lt;span class="c"&gt;# For Alpine Linux (no bash by default)&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; myapp sh

&lt;span class="c"&gt;# Run specific commands inside&lt;/span&gt;
docker &lt;span class="nb"&gt;exec &lt;/span&gt;myapp &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt; /app
docker &lt;span class="nb"&gt;exec &lt;/span&gt;myapp &lt;span class="nb"&gt;cat&lt;/span&gt; /app/config.json
docker &lt;span class="nb"&gt;exec &lt;/span&gt;myapp &lt;span class="nb"&gt;env&lt;/span&gt; | &lt;span class="nb"&gt;grep &lt;/span&gt;DB_

&lt;span class="c"&gt;# If the container keeps crashing, override entrypoint to debug&lt;/span&gt;
docker run &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;--entrypoint&lt;/span&gt; bash myapp
docker run &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;--entrypoint&lt;/span&gt; sh myapp  &lt;span class="c"&gt;# Alpine&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 5: Check Resource Usage
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Live stats for a specific container&lt;/span&gt;
docker stats myapp

&lt;span class="c"&gt;# Live stats for all containers&lt;/span&gt;
docker stats

&lt;span class="c"&gt;# One-time snapshot (no streaming)&lt;/span&gt;
docker stats &lt;span class="nt"&gt;--no-stream&lt;/span&gt;

&lt;span class="c"&gt;# Check process list inside container&lt;/span&gt;
docker top myapp

&lt;span class="c"&gt;# Check resource limits set on container&lt;/span&gt;
docker inspect &lt;span class="nt"&gt;--format&lt;/span&gt; &lt;span class="s1"&gt;'{{json .HostConfig.Memory}}'&lt;/span&gt; myapp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 6: Network Inspection
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Inspect a network&lt;/span&gt;
docker network inspect app-network

&lt;span class="c"&gt;# Check container's network settings&lt;/span&gt;
docker inspect &lt;span class="nt"&gt;--format&lt;/span&gt; &lt;span class="s1"&gt;'{{json .NetworkSettings}}'&lt;/span&gt; myapp | jq &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="c"&gt;# Test DNS resolution from inside the container&lt;/span&gt;
docker &lt;span class="nb"&gt;exec &lt;/span&gt;myapp nslookup postgres
docker &lt;span class="nb"&gt;exec &lt;/span&gt;myapp nslookup google.com

&lt;span class="c"&gt;# Test connectivity&lt;/span&gt;
docker &lt;span class="nb"&gt;exec &lt;/span&gt;myapp curl &lt;span class="nt"&gt;-v&lt;/span&gt; http://api-service:3000/health
docker &lt;span class="nb"&gt;exec &lt;/span&gt;myapp ping &lt;span class="nt"&gt;-c&lt;/span&gt; 3 postgres

&lt;span class="c"&gt;# Check which networks the container is on&lt;/span&gt;
docker inspect &lt;span class="nt"&gt;--format&lt;/span&gt; &lt;span class="s1"&gt;'{{json .NetworkSettings.Networks}}'&lt;/span&gt; myapp | jq &lt;span class="s1"&gt;'keys'&lt;/span&gt;

&lt;span class="c"&gt;# Show port mappings&lt;/span&gt;
docker port myapp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 7: Check Events
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# All events in the last hour&lt;/span&gt;
docker events &lt;span class="nt"&gt;--since&lt;/span&gt; 1h

&lt;span class="c"&gt;# Filter by container&lt;/span&gt;
docker events &lt;span class="nt"&gt;--since&lt;/span&gt; 1h &lt;span class="nt"&gt;--filter&lt;/span&gt; &lt;span class="nv"&gt;container&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;myapp

&lt;span class="c"&gt;# Filter by event type&lt;/span&gt;
docker events &lt;span class="nt"&gt;--filter&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;container &lt;span class="nt"&gt;--filter&lt;/span&gt; &lt;span class="nv"&gt;event&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;oom

&lt;span class="c"&gt;# Watch events live&lt;/span&gt;
docker events

&lt;span class="c"&gt;# Events by image&lt;/span&gt;
docker events &lt;span class="nt"&gt;--filter&lt;/span&gt; &lt;span class="nv"&gt;image&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;myapp:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 8: Full Configuration Inspect
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Full JSON inspection&lt;/span&gt;
docker inspect myapp | jq &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="c"&gt;# Specific fields&lt;/span&gt;
docker inspect myapp | jq &lt;span class="s1"&gt;'.[0].Config.Env'&lt;/span&gt;        &lt;span class="c"&gt;# Environment variables&lt;/span&gt;
docker inspect myapp | jq &lt;span class="s1"&gt;'.[0].Mounts'&lt;/span&gt;             &lt;span class="c"&gt;# Volume mounts&lt;/span&gt;
docker inspect myapp | jq &lt;span class="s1"&gt;'.[0].HostConfig'&lt;/span&gt;         &lt;span class="c"&gt;# Host config (ports, limits)&lt;/span&gt;
docker inspect myapp | jq &lt;span class="s1"&gt;'.[0].NetworkSettings'&lt;/span&gt;    &lt;span class="c"&gt;# Network information&lt;/span&gt;

&lt;span class="c"&gt;# Image inspection&lt;/span&gt;
docker inspect myimage:tag | jq &lt;span class="s1"&gt;'.[0].Config.Cmd'&lt;/span&gt;
docker inspect myimage:tag | jq &lt;span class="s1"&gt;'.[0].Config.Entrypoint'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Do not include debugging tools in production images — they increase image size and expand the attack surface. Instead, maintain a separate debug image built from your production image plus debugging tools.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Quick Diagnosis Reference
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Problem Symptom&lt;/th&gt;
&lt;th&gt;First Command&lt;/th&gt;
&lt;th&gt;Common Fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Container not running&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker ps -a&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Check exit code, check logs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;App not responding&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker logs myapp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Look for errors in startup output&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Containers cannot communicate&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker network inspect&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Add both to the same user-defined network&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data not persisting&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;docker inspect&lt;/code&gt; (Mounts section)&lt;/td&gt;
&lt;td&gt;Add a volume mount&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;High memory usage&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker stats&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Add &lt;code&gt;--memory&lt;/code&gt; limit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Slow builds&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker build --no-cache&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Fix Dockerfile layer order&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Port not accessible&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker port myapp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Add &lt;code&gt;-p&lt;/code&gt; flag, check binding&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Container keeps restarting&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker logs myapp --tail 20&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Fix application error, check exit code&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Common Docker Interview Questions
&lt;/h2&gt;

&lt;p&gt;The following 15 questions cover the most important Docker topics you will encounter in engineering interviews. Each includes a concise answer, a detailed explanation, and common pitfalls to avoid.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Q1. What is the difference between a Docker image and a container?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A Docker image is an immutable, read-only template made of layered filesystems containing everything needed to run an application. A container is a running instance of that image — it adds a thin writable layer on top. Multiple containers can run from the same image simultaneously.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Key point:&lt;/strong&gt; Image = blueprint (immutable). Container = running instance (has a writable layer).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Red flag:&lt;/strong&gt; Saying "image and container are the same thing" immediately signals a foundational misunderstanding.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Q2. How does Docker image layer caching work?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Docker caches each instruction's result as a layer. On rebuild, if an instruction and its inputs are unchanged, Docker reuses the cached layer and skips re-execution. Once any layer is invalidated, all subsequent layers are rebuilt. This is why Dockerfile instruction order matters — place rarely-changing instructions (install dependencies) before frequently-changing ones (copy source code).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Good - npm install only re-runs when package.json changes&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm ci
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .   # source changes only invalidate this layer and below&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Red flag:&lt;/strong&gt; Not structuring Dockerfiles to exploit layer caching. A 3-minute &lt;code&gt;npm install&lt;/code&gt; running on every CI build is an immediate code review concern.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Q3. What are multi-stage builds and why use them?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Multi-stage builds use multiple &lt;code&gt;FROM&lt;/code&gt; statements in one Dockerfile. You compile/build in a full environment, then &lt;code&gt;COPY --from=stage&lt;/code&gt; only the compiled artifacts into a minimal runtime image. This keeps build tools out of the final image, dramatically reducing size and attack surface.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;golang:1.21&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;builder&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; go.mod go.sum ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;go mod download
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nv"&gt;CGO_ENABLED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 go build &lt;span class="nt"&gt;-o&lt;/span&gt; server .

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;scratch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;runtime&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /app/server /server&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["/server"]&lt;/span&gt;
&lt;span class="c"&gt;# Result: ~10 MB image vs ~800 MB with full Go toolchain&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Red flag:&lt;/strong&gt; Not knowing that you can have more than two stages, or that you can target specific stages with &lt;code&gt;docker build --target=stagename&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Q4. What is the difference between CMD and ENTRYPOINT?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ENTRYPOINT&lt;/code&gt; sets the fixed executable that always runs. &lt;code&gt;CMD&lt;/code&gt; provides default arguments that can be overridden at runtime. Together: &lt;code&gt;ENTRYPOINT&lt;/code&gt; is "what to run" and &lt;code&gt;CMD&lt;/code&gt; is "with what default arguments." &lt;code&gt;CMD&lt;/code&gt; alone is fully overridable by passing arguments to &lt;code&gt;docker run&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["python", "app.py"]   # always runs python app.py&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["--port", "8080"]             # default arg, overridable&lt;/span&gt;

&lt;span class="c"&gt;# docker run myapp                  → python app.py --port 8080&lt;/span&gt;
&lt;span class="c"&gt;# docker run myapp --port 9090      → python app.py --port 9090&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Red flag:&lt;/strong&gt; Using shell form for CMD/ENTRYPOINT (&lt;code&gt;CMD node server.js&lt;/code&gt;). Shell form runs your command via &lt;code&gt;/bin/sh -c&lt;/code&gt;, meaning the shell is PID 1 — not your application. SIGTERM is not forwarded, breaking graceful shutdown.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Q5. What is the difference between volumes and bind mounts?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Bind mounts link a specific host path into the container — you control the location. Named volumes are managed by Docker, stored at &lt;code&gt;/var/lib/docker/volumes/&lt;/code&gt;. Named volumes are preferred in production for persistent data (databases). Bind mounts are preferred in development for live code reloading.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Storage&lt;/th&gt;
&lt;th&gt;Best For&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Named Volume&lt;/td&gt;
&lt;td&gt;Docker-managed&lt;/td&gt;
&lt;td&gt;DB data, persistent app data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bind Mount&lt;/td&gt;
&lt;td&gt;Specific host path&lt;/td&gt;
&lt;td&gt;Development hot reload, config files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;tmpfs&lt;/td&gt;
&lt;td&gt;Host RAM (never on disk)&lt;/td&gt;
&lt;td&gt;Secrets, temporary cache&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Red flag:&lt;/strong&gt; Using bind mounts in production — they create host-container coupling and can expose host files if misconfigured.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Q6. Explain Docker networking — bridge, host, and overlay.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;bridge&lt;/strong&gt; (default): Isolated virtual network on the host. Containers communicate via container names on user-defined bridge networks (with automatic DNS). Requires &lt;code&gt;-p&lt;/code&gt; to expose ports to the host.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;host&lt;/strong&gt;: Container shares the host's network stack directly. No NAT overhead, but no network isolation. Only works on Linux.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;overlay&lt;/strong&gt;: Spans multiple Docker hosts using VXLAN tunneling. Required for Docker Swarm multi-host communication.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The default bridge network does not provide automatic DNS — containers must use IPs. User-defined bridge networks resolve container names via Docker's embedded DNS at &lt;code&gt;127.0.0.11&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Red flag:&lt;/strong&gt; Using the default bridge network in production. Default bridge has no DNS and all containers on it can communicate with each other — no isolation.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Q7. What are Dockerfile best practices?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Key best practices:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use minimal base images (alpine, distroless)&lt;/li&gt;
&lt;li&gt;Use multi-stage builds for compiled languages and build-step frameworks&lt;/li&gt;
&lt;li&gt;Exploit layer caching — copy dependency manifests before source code&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;.dockerignore&lt;/code&gt; to exclude &lt;code&gt;node_modules&lt;/code&gt;, &lt;code&gt;.git&lt;/code&gt;, &lt;code&gt;.env&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Run as a non-root user (&lt;code&gt;USER node&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Use exec form for CMD and ENTRYPOINT (&lt;code&gt;["node", "server.js"]&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Combine &lt;code&gt;RUN&lt;/code&gt; commands to minimize layers, clean up in the same instruction&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;HEALTHCHECK&lt;/code&gt; for production images&lt;/li&gt;
&lt;li&gt;Pin base image versions — avoid &lt;code&gt;:latest&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Never pass secrets via &lt;code&gt;ARG&lt;/code&gt; or &lt;code&gt;ENV&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;&lt;strong&gt;Q8. What does &lt;code&gt;depends_on&lt;/code&gt; do in Docker Compose — does it guarantee the service is ready?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;depends_on&lt;/code&gt; controls startup order — it ensures the dependent container starts before the dependent service. By default it only waits for the container to start (&lt;code&gt;service_started&lt;/code&gt;), not for the application inside to be ready.&lt;/p&gt;

&lt;p&gt;To actually wait for a service to be ready, combine &lt;code&gt;depends_on&lt;/code&gt; with &lt;code&gt;condition: service_healthy&lt;/code&gt; and a proper &lt;code&gt;healthcheck&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;api&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;postgres&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;service_healthy&lt;/span&gt;   &lt;span class="c1"&gt;# waits for healthcheck to pass&lt;/span&gt;

&lt;span class="na"&gt;postgres&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres:15&lt;/span&gt;
  &lt;span class="na"&gt;healthcheck&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CMD-SHELL"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pg_isready&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-U&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;postgres"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;5s&lt;/span&gt;
    &lt;span class="na"&gt;retries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Red flag:&lt;/strong&gt; Using &lt;code&gt;depends_on&lt;/code&gt; without &lt;code&gt;condition: service_healthy&lt;/code&gt; and then being surprised when the application crashes on startup because the database was not ready.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Q9. How do you secure a Docker container in production?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A hardened container uses multiple layers of defense:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--user&lt;/span&gt; 1000:1000 &lt;span class="se"&gt;\ &lt;/span&gt;             &lt;span class="c"&gt;# non-root user&lt;/span&gt;
  &lt;span class="nt"&gt;--cap-drop&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ALL &lt;span class="se"&gt;\ &lt;/span&gt;               &lt;span class="c"&gt;# drop all capabilities&lt;/span&gt;
  &lt;span class="nt"&gt;--cap-add&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;NET_BIND_SERVICE &lt;span class="se"&gt;\ &lt;/span&gt;   &lt;span class="c"&gt;# add only what is needed&lt;/span&gt;
  &lt;span class="nt"&gt;--read-only&lt;/span&gt; &lt;span class="se"&gt;\ &lt;/span&gt;                  &lt;span class="c"&gt;# read-only root filesystem&lt;/span&gt;
  &lt;span class="nt"&gt;--tmpfs&lt;/span&gt; /tmp:size&lt;span class="o"&gt;=&lt;/span&gt;50m &lt;span class="se"&gt;\ &lt;/span&gt;        &lt;span class="c"&gt;# writable RAM-backed temp&lt;/span&gt;
  &lt;span class="nt"&gt;--no-new-privileges&lt;/span&gt; &lt;span class="se"&gt;\ &lt;/span&gt;          &lt;span class="c"&gt;# prevent privilege escalation&lt;/span&gt;
  &lt;span class="nt"&gt;--security-opt&lt;/span&gt; &lt;span class="nv"&gt;seccomp&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/etc/docker/seccomp.json &lt;span class="se"&gt;\&lt;/span&gt;
  myapp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Checklist: non-root USER, no &lt;code&gt;--privileged&lt;/code&gt;, &lt;code&gt;--read-only&lt;/code&gt; filesystem, drop all capabilities, minimal base image, no &lt;code&gt;docker.sock&lt;/code&gt; mount, scan with Trivy, use BuildKit secret mounts (never &lt;code&gt;ARG&lt;/code&gt;/&lt;code&gt;ENV&lt;/code&gt; for secrets).&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Q10. What is the difference between &lt;code&gt;docker stop&lt;/code&gt; and &lt;code&gt;docker kill&lt;/code&gt;?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;docker stop&lt;/code&gt; sends SIGTERM to allow graceful shutdown, waits 10 seconds (configurable), then sends SIGKILL. &lt;code&gt;docker kill&lt;/code&gt; immediately sends SIGKILL without waiting.&lt;/p&gt;

&lt;p&gt;Graceful shutdown matters: SIGTERM lets applications finish active requests, close database connections, and flush logs. Applications should handle SIGTERM explicitly. Always use &lt;code&gt;docker stop&lt;/code&gt; in production scripts — &lt;code&gt;docker kill&lt;/code&gt; can cause data corruption if the app is mid-write.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Q11. What is BuildKit and what advantages does it provide?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;BuildKit is Docker's next-generation build engine (default since Docker 23.0). Key advantages over the classic builder:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Parallel stage execution in multi-stage builds&lt;/li&gt;
&lt;li&gt;Improved layer caching (including registry-based cache)&lt;/li&gt;
&lt;li&gt;Secret mounts (&lt;code&gt;RUN --mount=type=secret&lt;/code&gt;) — secrets are never stored in any layer&lt;/li&gt;
&lt;li&gt;SSH agent forwarding for private repositories&lt;/li&gt;
&lt;li&gt;Multi-platform builds with buildx&lt;/li&gt;
&lt;li&gt;Significantly faster build times
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# BuildKit secret mount (secret is never baked into image)&lt;/span&gt;
RUN &lt;span class="nt"&gt;--mount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;secret,id&lt;span class="o"&gt;=&lt;/span&gt;npmrc &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nb"&gt;cp&lt;/span&gt; /run/secrets/npmrc ~/.npmrc &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nb"&gt;rm&lt;/span&gt; ~/.npmrc

docker build &lt;span class="nt"&gt;--secret&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;npmrc,src&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;/.npmrc &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Red flag:&lt;/strong&gt; Not knowing BuildKit secret mounts exist, and instead passing secrets via &lt;code&gt;ARG&lt;/code&gt; or &lt;code&gt;ENV&lt;/code&gt;, which appear in &lt;code&gt;docker history&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Q12. How do you implement zero-downtime deployments with Docker?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Use blue-green or rolling update strategies. The key principle: start the new container and verify it is healthy before stopping the old one — never the reverse.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Blue-green pattern&lt;/span&gt;
&lt;span class="c"&gt;# 1. Start new (green) container&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; myapp-green &lt;span class="nt"&gt;--network&lt;/span&gt; app myapp:new

&lt;span class="c"&gt;# 2. Health check green&lt;/span&gt;
docker &lt;span class="nb"&gt;exec &lt;/span&gt;myapp-green curl &lt;span class="nt"&gt;-sf&lt;/span&gt; http://localhost:3000/health

&lt;span class="c"&gt;# 3. Switch traffic (update nginx upstream, then reload)&lt;/span&gt;
&lt;span class="c"&gt;# nginx -s reload&lt;/span&gt;

&lt;span class="c"&gt;# 4. Stop old (blue)&lt;/span&gt;
docker stop myapp-blue &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; docker &lt;span class="nb"&gt;rm &lt;/span&gt;myapp-blue
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In Kubernetes, use rolling updates with &lt;code&gt;maxUnavailable: 0&lt;/code&gt; and &lt;code&gt;maxSurge: 1&lt;/code&gt; combined with readiness probes.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Q13. How do you handle secrets in Docker?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Never store secrets in &lt;code&gt;ARG&lt;/code&gt;, &lt;code&gt;ENV&lt;/code&gt;, or files copied into an image. The correct approaches are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Build-time secrets:&lt;/strong&gt; Use BuildKit &lt;code&gt;--mount=type=secret&lt;/code&gt; — the secret is available during the build RUN instruction but is never stored in any image layer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Runtime secrets:&lt;/strong&gt; Inject via environment variables at &lt;code&gt;docker run&lt;/code&gt; time (values are not in the image), Docker Compose secrets (mounted as files at &lt;code&gt;/run/secrets/&lt;/code&gt;), or external secret managers (AWS Secrets Manager, HashiCorp Vault).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Verify: &lt;code&gt;docker history myimage&lt;/code&gt; and &lt;code&gt;docker inspect myimage&lt;/code&gt; should never reveal secret values.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Q14. What is the difference between &lt;code&gt;docker exec&lt;/code&gt; and &lt;code&gt;docker attach&lt;/code&gt;?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;docker exec&lt;/code&gt; starts a new process inside a running container — it is safe for debugging. &lt;code&gt;docker attach&lt;/code&gt; connects your terminal to the container's existing PID 1 process. If you press Ctrl+C while attached, you send SIGINT to PID 1 and stop the container.&lt;/p&gt;

&lt;p&gt;Always use &lt;code&gt;docker exec -it mycontainer /bin/sh&lt;/code&gt; for debugging. Use &lt;code&gt;docker attach&lt;/code&gt; only when you specifically need to interact with PID 1, and detach with Ctrl+P, Ctrl+Q.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Q15. How do you optimize Docker builds in CI/CD for speed?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Five key techniques:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Registry-based layer caching&lt;/strong&gt; — reuse layers from previous builds with &lt;code&gt;--cache-from&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dockerfile layer order&lt;/strong&gt; — dependency installation before source code copy&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;BuildKit inline cache&lt;/strong&gt; — export cache to registry with &lt;code&gt;--cache-to type=registry,mode=max&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-stage parallelism&lt;/strong&gt; — BuildKit builds independent stages in parallel&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Actions/GitLab cache&lt;/strong&gt; — use &lt;code&gt;type=gha&lt;/code&gt; cache for persistent caching between runs
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# GitHub Actions example&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/build-push-action@v5&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;cache-from&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;type=gha&lt;/span&gt;
    &lt;span class="na"&gt;cache-to&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;type=gha,mode=max&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A well-optimized pipeline can reduce build times from 10 minutes to under 2 minutes.&lt;/p&gt;

&lt;p&gt;Prepping for interviews? See &lt;a href="https://opscanopy.com/learn/guides/docker-interview-questions/" rel="noopener noreferrer"&gt;Docker Interview Questions&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Complete Docker Cheat Sheet
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Container Commands
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker run -d -p 8080:80 --name app nginx&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run detached with port mapping and name&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker run -it --rm ubuntu bash&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Interactive shell, auto-remove on exit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker run -e ENV=prod --env-file .env app&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;With environment variables&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker run -v vol:/data --memory=512m app&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;With volume and memory limit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker ps&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List running containers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker ps -a&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List ALL containers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Custom format output&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker stop myapp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Graceful stop (SIGTERM then SIGKILL)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker kill myapp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Immediate kill (SIGKILL)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker rm myapp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove stopped container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker rm -f myapp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Force remove running container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker logs -f --tail 100 myapp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Follow last 100 log lines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker exec -it myapp bash&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Enter container shell&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker exec myapp cat /app/config.json&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run command in container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker cp myapp:/app/file.txt ./&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Copy file from container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker cp ./file.txt myapp:/app/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Copy file to container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker stats --no-stream&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Snapshot resource usage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker top myapp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show processes in container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker inspect myapp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Full container JSON information&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker inspect --format '{{.NetworkSettings.IPAddress}}' myapp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Get container IP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker update --restart=always --memory=1g myapp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Update running container settings&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker rename myapp newname&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Rename container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker pause myapp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Freeze container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker unpause myapp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Unfreeze container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker container prune&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove all stopped containers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker events --filter type=container&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Live container events&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Image Commands
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker pull nginx:1.25-alpine&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pull specific tag&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker pull ubuntu@sha256:abc123&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pull by digest (immutable)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker images&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List local images&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker build -t myapp:v1.0 .&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Build from Dockerfile in current directory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker build -f Dockerfile.prod -t myapp:prod .&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Use specific Dockerfile&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker build --no-cache -t myapp .&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Force fresh build&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker build --build-arg NODE_ENV=prod -t myapp .&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;With build argument&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker build --target production -t myapp .&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Build specific stage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker tag myapp:latest registry/myapp:v1.0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Tag for registry&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker push registry/myapp:v1.0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Push to registry&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker rmi myapp:latest&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove image&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker history myapp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show image layers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker history --no-trunc myapp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Full layer commands&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker inspect myapp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Image JSON details&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker save -o myapp.tar myapp:latest&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Export to tar archive&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker load -i myapp.tar&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Import from tar archive&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker image prune&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove dangling images&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker image prune -a&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove all unused images&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker system df&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Disk usage summary&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker system prune -af --volumes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Full cleanup of all unused resources&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Network Commands
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker network ls&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List networks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker network create mynet&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create bridge network&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker network create --internal secure&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Internal network (no internet access)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker network inspect mynet&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Network details&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker network connect mynet mycontainer&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Connect running container to network&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker network disconnect mynet mycontainer&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Disconnect container from network&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker network rm mynet&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove network&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker network prune&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove unused networks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker run --network mynet myapp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run in specific network&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker run --network host myapp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Use host network&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker run --network none myapp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No networking&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker run -p 8080:80 myapp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Publish port&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker run -p 127.0.0.1:8080:80 myapp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Bind to localhost only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker port myapp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show port mappings&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Volume Commands
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker volume create mydata&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create named volume&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker volume ls&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List volumes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker volume inspect mydata&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Volume details&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker volume rm mydata&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove volume&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker volume prune&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove unused volumes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker run -v mydata:/app/data myapp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Mount named volume&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker run -v $(pwd):/app myapp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Bind mount current directory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker run -v /app/node_modules myapp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Anonymous volume&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker run --mount type=volume,source=mydata,target=/data myapp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Mount syntax&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker run --mount type=bind,source=$(pwd),target=/app myapp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Bind mount syntax&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker run --mount type=tmpfs,target=/tmp myapp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;tmpfs mount&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker run -v /host/path:/container/path:ro myapp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Read-only mount&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Docker Compose Commands
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker compose up -d&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Start all services detached&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker compose up -d --build&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Rebuild and start&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker compose up -d --scale api=3&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Start with 3 api replicas&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker compose down&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Stop and remove containers and networks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker compose down -v&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Also remove volumes (destructive — use with care)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker compose ps&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List services&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker compose logs -f api&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Follow api logs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker compose exec api bash&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Shell into running service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker compose run --rm api npm test&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;One-off command in new container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker compose build --no-cache&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Force rebuild&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker compose pull&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pull latest images&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker compose config&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Validate and show resolved configuration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker compose restart api&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Restart specific service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker compose --profile dev up -d&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Start with profile&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker compose -f base.yml -f prod.yml up -d&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Multiple compose files&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Dockerfile Quick Reference
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Instruction&lt;/th&gt;
&lt;th&gt;Syntax&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;FROM&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;FROM node:18-alpine AS builder&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Base image&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;LABEL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;LABEL version="1.0" maintainer="x@y.com"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Metadata&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RUN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;RUN apt-get update &amp;amp;&amp;amp; apt-get install -y curl&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Execute during build&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CMD&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CMD ["node", "server.js"]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Default command (overridable)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ENTRYPOINT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ENTRYPOINT ["npm", "start"]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Fixed executable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;COPY&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;COPY --chown=node:node . /app&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Copy files from build context&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ADD&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ADD archive.tar.gz /app/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Copy and auto-extract archives&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;WORKDIR&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;WORKDIR /app&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Set working directory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ENV&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ENV NODE_ENV=production PORT=3000&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Runtime environment variable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ARG&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ARG NODE_VERSION=18&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Build-time variable only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;EXPOSE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;EXPOSE 3000&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Document port (does not publish)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;VOLUME&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;VOLUME /app/data&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Declare mount point&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;USER&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;USER node&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Switch to non-root user&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;HEALTHCHECK&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;HEALTHCHECK --interval=30s CMD curl -f http://localhost/health&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Health monitoring&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;STOPSIGNAL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;STOPSIGNAL SIGQUIT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Override stop signal&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Power One-Liners
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;One-liner&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker system prune -af --volumes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Complete cleanup&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker stop $(docker ps -q)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Stop all running containers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker rm $(docker ps -aq)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove all containers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker rmi $(docker images -q)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove all images&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker rm $(docker ps -aq -f status=exited)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove only exited containers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker inspect --format '{{.NetworkSettings.IPAddress}}' myapp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Get container IP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker inspect --format '{{.State.ExitCode}}' myapp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Get exit code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;`docker logs myapp 2&amp;gt;&amp;amp;1 \&lt;/td&gt;
&lt;td&gt;grep -i error`&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker logs --since 1h --tail 100 myapp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Recent logs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker events --filter 'event=die'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Watch for container deaths&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;`docker images --format "{{.Size}}\t{{.Repository}}:{{.Tag}}" \&lt;/td&gt;
&lt;td&gt;sort -h`&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker run --rm -v $(pwd):/app node:18-alpine npm install&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run npm install via Docker&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker exec myapp env&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List container environment variables&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker diff myapp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show filesystem changes in container&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Production Workflows
&lt;/h2&gt;

&lt;p&gt;Real-world Docker workflows used daily in production environments.&lt;/p&gt;

&lt;h3&gt;
  
  
  Daily DevOps Docker Checklist
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# ===== Morning Health Check =====&lt;/span&gt;
docker ps                                    &lt;span class="c"&gt;# Are all containers running?&lt;/span&gt;
docker stats &lt;span class="nt"&gt;--no-stream&lt;/span&gt;                     &lt;span class="c"&gt;# Is resource usage normal?&lt;/span&gt;
docker logs myapp &lt;span class="nt"&gt;--since&lt;/span&gt; 8h | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-iE&lt;/span&gt; &lt;span class="s1"&gt;'error|fatal|panic'&lt;/span&gt;  &lt;span class="c"&gt;# Overnight errors?&lt;/span&gt;
&lt;span class="nb"&gt;df&lt;/span&gt; &lt;span class="nt"&gt;-h&lt;/span&gt; /var/lib/docker                        &lt;span class="c"&gt;# Is disk space OK?&lt;/span&gt;

&lt;span class="c"&gt;# ===== Deployment Workflow =====&lt;/span&gt;
&lt;span class="c"&gt;# 1. Build&lt;/span&gt;
&lt;span class="nv"&gt;VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git describe &lt;span class="nt"&gt;--tags&lt;/span&gt; &lt;span class="nt"&gt;--always&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
docker build &lt;span class="nt"&gt;-t&lt;/span&gt; myapp:&lt;span class="nv"&gt;$VERSION&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="c"&gt;# 2. Test&lt;/span&gt;
docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; myapp:&lt;span class="nv"&gt;$VERSION&lt;/span&gt; npm &lt;span class="nb"&gt;test&lt;/span&gt;

&lt;span class="c"&gt;# 3. Tag and push&lt;/span&gt;
docker tag myapp:&lt;span class="nv"&gt;$VERSION&lt;/span&gt; registry/myapp:&lt;span class="nv"&gt;$VERSION&lt;/span&gt;
docker push registry/myapp:&lt;span class="nv"&gt;$VERSION&lt;/span&gt;

&lt;span class="c"&gt;# 4. Deploy (rolling update on server)&lt;/span&gt;
docker pull registry/myapp:&lt;span class="nv"&gt;$VERSION&lt;/span&gt;
docker stop myapp-old &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true
&lt;/span&gt;docker rename myapp myapp-old &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true
&lt;/span&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; myapp &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--restart&lt;/span&gt; unless-stopped &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-p&lt;/span&gt; 80:3000 &lt;span class="se"&gt;\&lt;/span&gt;
  registry/myapp:&lt;span class="nv"&gt;$VERSION&lt;/span&gt;

&lt;span class="c"&gt;# 5. Verify&lt;/span&gt;
&lt;span class="nb"&gt;sleep &lt;/span&gt;5 &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; docker ps | &lt;span class="nb"&gt;grep &lt;/span&gt;myapp
curl &lt;span class="nt"&gt;-f&lt;/span&gt; http://localhost:80/health

&lt;span class="c"&gt;# 6. Clean up old container&lt;/span&gt;
docker &lt;span class="nb"&gt;rm &lt;/span&gt;myapp-old
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Rollback Workflow
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;PREV_VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"v1.2.2"&lt;/span&gt;
docker pull registry/myapp:&lt;span class="nv"&gt;$PREV_VERSION&lt;/span&gt;
docker stop myapp &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; docker &lt;span class="nb"&gt;rm &lt;/span&gt;myapp
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; myapp registry/myapp:&lt;span class="nv"&gt;$PREV_VERSION&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; Never run &lt;code&gt;docker rmi&lt;/code&gt; on the last known-good image until the new deployment has been stable for at least 30 minutes. Tag the last working image as &lt;code&gt;:stable&lt;/code&gt; or &lt;code&gt;:prev&lt;/code&gt; to enable instant rollback.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Monthly Maintenance
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Clean up unused resources older than 30 days&lt;/span&gt;
docker system prune &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nt"&gt;--filter&lt;/span&gt; &lt;span class="s2"&gt;"until=720h"&lt;/span&gt;
docker volume prune &lt;span class="nt"&gt;-f&lt;/span&gt;

&lt;span class="c"&gt;# Update base images and rebuild with latest security patches&lt;/span&gt;
docker pull node:18-alpine
docker build &lt;span class="nt"&gt;--no-cache&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Zero-Downtime Deployment Pattern
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="c"&gt;# zero-downtime-deploy.sh&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;

&lt;span class="nv"&gt;NEW_IMAGE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"registry/myapp:&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;CONTAINER_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"myapp"&lt;/span&gt;
&lt;span class="nv"&gt;BLUE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CONTAINER_NAME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-blue"&lt;/span&gt;
&lt;span class="nv"&gt;GREEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CONTAINER_NAME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-green"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Pulling new image..."&lt;/span&gt;
docker pull &lt;span class="nv"&gt;$NEW_IMAGE&lt;/span&gt;

&lt;span class="c"&gt;# Determine which container is active&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;docker ps | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="nv"&gt;$BLUE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nv"&gt;ACTIVE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$BLUE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;INACTIVE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$GREEN&lt;/span&gt;
&lt;span class="k"&gt;else
  &lt;/span&gt;&lt;span class="nv"&gt;ACTIVE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$GREEN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;INACTIVE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$BLUE&lt;/span&gt;
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Starting &lt;/span&gt;&lt;span class="nv"&gt;$INACTIVE&lt;/span&gt;&lt;span class="s2"&gt;..."&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="nv"&gt;$INACTIVE&lt;/span&gt; &lt;span class="nt"&gt;--network&lt;/span&gt; app &lt;span class="nv"&gt;$NEW_IMAGE&lt;/span&gt;

&lt;span class="c"&gt;# Health check loop&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;i &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;1..30&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  if &lt;/span&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nv"&gt;$INACTIVE&lt;/span&gt; curl &lt;span class="nt"&gt;-sf&lt;/span&gt; http://localhost:3000/health&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Health check passed!"&lt;/span&gt;
    &lt;span class="nb"&gt;break
  &lt;/span&gt;&lt;span class="k"&gt;fi
  &lt;/span&gt;&lt;span class="nb"&gt;sleep &lt;/span&gt;2
&lt;span class="k"&gt;done&lt;/span&gt;

&lt;span class="c"&gt;# Switch traffic (update nginx/load balancer upstream here)&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Switching traffic to &lt;/span&gt;&lt;span class="nv"&gt;$INACTIVE&lt;/span&gt;&lt;span class="s2"&gt;..."&lt;/span&gt;

&lt;span class="c"&gt;# Stop old container&lt;/span&gt;
docker stop &lt;span class="nv"&gt;$ACTIVE&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; docker &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nv"&gt;$ACTIVE&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Deployment complete! Active: &lt;/span&gt;&lt;span class="nv"&gt;$INACTIVE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Incident Response Commands
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Container crashed — investigate&lt;/span&gt;
docker logs myapp &lt;span class="nt"&gt;--tail&lt;/span&gt; 200
docker inspect myapp | jq &lt;span class="s1"&gt;'.[0].State'&lt;/span&gt;  &lt;span class="c"&gt;# Check exit code, OOMKilled flag&lt;/span&gt;
docker events &lt;span class="nt"&gt;--since&lt;/span&gt; 1h

&lt;span class="c"&gt;# High CPU or Memory&lt;/span&gt;
docker stats &lt;span class="nt"&gt;--no-stream&lt;/span&gt;
docker &lt;span class="nb"&gt;exec &lt;/span&gt;myapp top

&lt;span class="c"&gt;# Network issues&lt;/span&gt;
docker &lt;span class="nb"&gt;exec &lt;/span&gt;myapp curl &lt;span class="nt"&gt;-v&lt;/span&gt; http://db:5432
docker network inspect myapp_default
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Docker Compose Patterns
&lt;/h2&gt;

&lt;p&gt;Battle-tested Compose patterns used in real production environments.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pattern 1: Base + Override (Development vs Production)
&lt;/h3&gt;

&lt;p&gt;The most common pattern — one base file with environment-specific overrides.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# docker-compose.yml (base — committed to git)&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;api&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${REGISTRY:-myapp}/api:${TAG:-latest}&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3000:3000"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;db&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres:15-alpine&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_DB&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myapp&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_USER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;user&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${DB_PASSWORD}&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;pgdata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;/var/lib/postgresql/data&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;redis&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis:7-alpine&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pgdata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

&lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# docker-compose.override.yml (development overrides — add to .gitignore)&lt;/span&gt;
&lt;span class="c1"&gt;# Auto-loaded when you run: docker compose up&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;api&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;                          &lt;span class="c1"&gt;# Build locally instead of pulling&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.:/app&lt;/span&gt;                        &lt;span class="c1"&gt;# Live code reload&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/app/node_modules&lt;/span&gt;             &lt;span class="c1"&gt;# Preserve installed node_modules&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;DEBUG&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;true"&lt;/span&gt;
      &lt;span class="na"&gt;NODE_ENV&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;development&lt;/span&gt;
      &lt;span class="na"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres://user:pass@db:5432/myapp&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm run dev&lt;/span&gt;              &lt;span class="c1"&gt;# Use dev server with hot reload&lt;/span&gt;
  &lt;span class="c1"&gt;# Development-only extras&lt;/span&gt;
  &lt;span class="na"&gt;mailhog&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mailhog/mailhog&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8025:8025"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;             &lt;span class="c1"&gt;# Fake email server for testing&lt;/span&gt;
  &lt;span class="na"&gt;redis-ui&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rediscommander/redis-commander&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8081:8081"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;profiles&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;debug&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;                 &lt;span class="c1"&gt;# Only starts with --profile debug&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# docker-compose.prod.yml (production overrides)&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;api&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
    &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;limits&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;cpus&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1.0"&lt;/span&gt;
          &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;512M&lt;/span&gt;
        &lt;span class="na"&gt;reservations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;cpus&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0.25"&lt;/span&gt;
          &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;256M&lt;/span&gt;
    &lt;span class="na"&gt;logging&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;driver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;json-file&lt;/span&gt;
      &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;max-size&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;10m"&lt;/span&gt;
        &lt;span class="na"&gt;max-file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3"&lt;/span&gt;
    &lt;span class="na"&gt;healthcheck&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CMD"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;curl"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-f"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:3000/health"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;30s&lt;/span&gt;
      &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10s&lt;/span&gt;
      &lt;span class="na"&gt;retries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
      &lt;span class="na"&gt;start_period&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;40s&lt;/span&gt;
  &lt;span class="na"&gt;nginx&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx:1.25-alpine&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;80:80"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;443:443"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./nginx/prod.conf:/etc/nginx/conf.d/default.conf:ro&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./ssl:/etc/ssl:ro&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Development (auto-loads override file)&lt;/span&gt;
docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt;

&lt;span class="c"&gt;# Production&lt;/span&gt;
docker compose &lt;span class="nt"&gt;-f&lt;/span&gt; docker-compose.yml &lt;span class="nt"&gt;-f&lt;/span&gt; docker-compose.prod.yml up &lt;span class="nt"&gt;-d&lt;/span&gt;

&lt;span class="c"&gt;# Validate the merged configuration&lt;/span&gt;
docker compose &lt;span class="nt"&gt;-f&lt;/span&gt; docker-compose.yml &lt;span class="nt"&gt;-f&lt;/span&gt; docker-compose.prod.yml config
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Pattern 2: Microservices with Profiles
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# Core services (always start)&lt;/span&gt;
  &lt;span class="na"&gt;gateway&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myapp/gateway:latest&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;80:80"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

  &lt;span class="na"&gt;auth-service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myapp/auth:latest&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

  &lt;span class="c1"&gt;# Optional services — start only with a profile&lt;/span&gt;
  &lt;span class="na"&gt;monitoring&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;prom/prometheus&lt;/span&gt;
    &lt;span class="na"&gt;profiles&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;monitoring&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;./prometheus.yml&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;/etc/prometheus/prometheus.yml&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;9090:9090"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

  &lt;span class="na"&gt;grafana&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;grafana/grafana&lt;/span&gt;
    &lt;span class="na"&gt;profiles&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;monitoring&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3001:3000"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

  &lt;span class="c1"&gt;# Integration test dependencies&lt;/span&gt;
  &lt;span class="na"&gt;test-db&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres:15-alpine&lt;/span&gt;
    &lt;span class="na"&gt;profiles&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;testing&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_DB&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;testdb&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;testpass&lt;/span&gt;

&lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Start core services only&lt;/span&gt;
docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt;

&lt;span class="c"&gt;# Start with monitoring stack&lt;/span&gt;
docker compose &lt;span class="nt"&gt;--profile&lt;/span&gt; monitoring up &lt;span class="nt"&gt;-d&lt;/span&gt;

&lt;span class="c"&gt;# Start with testing dependencies&lt;/span&gt;
docker compose &lt;span class="nt"&gt;--profile&lt;/span&gt; testing up &lt;span class="nt"&gt;-d&lt;/span&gt;

&lt;span class="c"&gt;# Start everything&lt;/span&gt;
docker compose &lt;span class="nt"&gt;--profile&lt;/span&gt; monitoring &lt;span class="nt"&gt;--profile&lt;/span&gt; testing up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Pattern 3: Dependency Management with Health Checks
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;db&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres:15-alpine&lt;/span&gt;
    &lt;span class="na"&gt;healthcheck&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CMD-SHELL"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pg_isready&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-U&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-d&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;myapp"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10s&lt;/span&gt;
      &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;5s&lt;/span&gt;
      &lt;span class="na"&gt;retries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
      &lt;span class="na"&gt;start_period&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;30s&lt;/span&gt;

  &lt;span class="na"&gt;redis&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis:7-alpine&lt;/span&gt;
    &lt;span class="na"&gt;healthcheck&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CMD"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;redis-cli"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ping"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10s&lt;/span&gt;
      &lt;span class="na"&gt;retries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;

  &lt;span class="na"&gt;api&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;db&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;service_healthy&lt;/span&gt;    &lt;span class="c1"&gt;# Wait for db health check to pass&lt;/span&gt;
      &lt;span class="na"&gt;redis&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;service_healthy&lt;/span&gt;    &lt;span class="c1"&gt;# Wait for redis health check to pass&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres://user:pass@db:5432/myapp&lt;/span&gt;
      &lt;span class="na"&gt;REDIS_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis://redis:6379&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Plain &lt;code&gt;depends_on&lt;/code&gt; only waits for the container to start — not for the service inside to be ready. Always combine with &lt;code&gt;condition: service_healthy&lt;/code&gt; and a proper &lt;code&gt;healthcheck&lt;/code&gt; block to avoid startup race conditions.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Docker Tools Ecosystem
&lt;/h2&gt;

&lt;p&gt;Essential tools that every Docker practitioner should know.&lt;/p&gt;

&lt;h3&gt;
  
  
  Portainer — Web GUI
&lt;/h3&gt;

&lt;p&gt;Web-based interface for managing Docker containers, images, networks, and volumes. Ideal for teams and for environments where a GUI is preferred.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker volume create portainer_data
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-p&lt;/span&gt; 9000:9000 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; portainer &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--restart&lt;/span&gt; always &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; /var/run/docker.sock:/var/run/docker.sock &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; portainer_data:/data &lt;span class="se"&gt;\&lt;/span&gt;
  portainer/portainer-ce:latest
&lt;span class="c"&gt;# Visit: http://localhost:9000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Lazydocker — Terminal UI
&lt;/h3&gt;

&lt;p&gt;Terminal UI for Docker. View containers, logs, stats, and images interactively without typing commands.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://raw.githubusercontent.com/jesseduffield/lazydocker/master/scripts/install_via_bash.sh | bash
lazydocker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  dive — Image Layer Analyzer
&lt;/h3&gt;

&lt;p&gt;Inspect Docker image layers interactively. Identify which files are added at each layer and find what is bloating image size.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Ubuntu&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;snap &lt;span class="nb"&gt;install &lt;/span&gt;dive
&lt;span class="c"&gt;# macOS&lt;/span&gt;
brew &lt;span class="nb"&gt;install &lt;/span&gt;dive
&lt;span class="c"&gt;# Analyze any image&lt;/span&gt;
dive myapp:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  hadolint — Dockerfile Linter
&lt;/h3&gt;

&lt;p&gt;Lint your Dockerfiles for best practice violations, security issues, and layer ordering problems.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Run via Docker (no installation required)&lt;/span&gt;
docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; hadolint/hadolint &amp;lt; Dockerfile

&lt;span class="c"&gt;# Install locally on macOS&lt;/span&gt;
brew &lt;span class="nb"&gt;install &lt;/span&gt;hadolint

hadolint Dockerfile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Trivy — Security Scanner
&lt;/h3&gt;

&lt;p&gt;Comprehensive vulnerability scanner for container images. Detects CVEs in OS packages, language dependencies, and misconfigurations.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-sfL&lt;/span&gt; https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh

&lt;span class="c"&gt;# Scan image for HIGH and CRITICAL CVEs only&lt;/span&gt;
trivy image &lt;span class="nt"&gt;--severity&lt;/span&gt; HIGH,CRITICAL myapp:latest

&lt;span class="c"&gt;# Fail CI on CRITICAL findings&lt;/span&gt;
trivy image &lt;span class="nt"&gt;--exit-code&lt;/span&gt; 1 &lt;span class="nt"&gt;--severity&lt;/span&gt; CRITICAL myapp:latest

&lt;span class="c"&gt;# Scan a Dockerfile for misconfigurations&lt;/span&gt;
trivy config Dockerfile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Watchtower — Automatic Container Updates
&lt;/h3&gt;

&lt;p&gt;Automatically updates running containers when new images are pushed to the registry. Recommended for development and staging environments only.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; watchtower &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; /var/run/docker.sock:/var/run/docker.sock &lt;span class="se"&gt;\&lt;/span&gt;
  containrrr/watchtower &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--interval&lt;/span&gt; 86400   &lt;span class="c"&gt;# Check daily&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ctop — Container Resource Monitor
&lt;/h3&gt;

&lt;p&gt;Real-time CPU, memory, network, and I/O metrics in a clean terminal interface — like &lt;code&gt;htop&lt;/code&gt; for Docker.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# macOS&lt;/span&gt;
brew &lt;span class="nb"&gt;install &lt;/span&gt;ctop

&lt;span class="c"&gt;# Any OS via Docker&lt;/span&gt;
docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-ti&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; ctop &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; /var/run/docker.sock:/var/run/docker.sock:ro &lt;span class="se"&gt;\&lt;/span&gt;
  quay.io/vektorlab/ctop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Tools Comparison
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Best For&lt;/th&gt;
&lt;th&gt;Free?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Portainer&lt;/td&gt;
&lt;td&gt;Web GUI&lt;/td&gt;
&lt;td&gt;Team management, beginners&lt;/td&gt;
&lt;td&gt;Yes (CE)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lazydocker&lt;/td&gt;
&lt;td&gt;Terminal UI&lt;/td&gt;
&lt;td&gt;Developers, quick inspection&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;dive&lt;/td&gt;
&lt;td&gt;CLI&lt;/td&gt;
&lt;td&gt;Image size optimization&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;hadolint&lt;/td&gt;
&lt;td&gt;Linter&lt;/td&gt;
&lt;td&gt;Dockerfile quality in CI&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;trivy&lt;/td&gt;
&lt;td&gt;Security&lt;/td&gt;
&lt;td&gt;CVE scanning, compliance&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;watchtower&lt;/td&gt;
&lt;td&gt;Daemon&lt;/td&gt;
&lt;td&gt;Auto-updates (dev/staging)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ctop&lt;/td&gt;
&lt;td&gt;TUI&lt;/td&gt;
&lt;td&gt;Real-time monitoring&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Docker Desktop&lt;/td&gt;
&lt;td&gt;GUI&lt;/td&gt;
&lt;td&gt;Mac/Windows developers&lt;/td&gt;
&lt;td&gt;Freemium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;VS Code Extension&lt;/td&gt;
&lt;td&gt;IDE&lt;/td&gt;
&lt;td&gt;Development workflow&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Recommended starter stack — install &lt;strong&gt;Lazydocker&lt;/strong&gt; for daily use, &lt;strong&gt;dive&lt;/strong&gt; to check image size before every push, and integrate &lt;strong&gt;hadolint&lt;/strong&gt; and &lt;strong&gt;trivy&lt;/strong&gt; into your CI/CD pipeline. These four tools will catch most common issues automatically.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Further Learning Resources
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Top Learning Resources
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Resource&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;URL&lt;/th&gt;
&lt;th&gt;Why It Is Valuable&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Docker Official Docs&lt;/td&gt;
&lt;td&gt;Documentation&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docs.docker.com&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Authoritative source, updated with every release&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Play with Docker&lt;/td&gt;
&lt;td&gt;Interactive Lab&lt;/td&gt;
&lt;td&gt;&lt;code&gt;labs.play-with-docker.com&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Free browser-based Docker playground — no installation needed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TechWorld with Nana&lt;/td&gt;
&lt;td&gt;YouTube&lt;/td&gt;
&lt;td&gt;&lt;code&gt;youtube.com/@TechWorldwithNana&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Best free Docker course on YouTube, extremely clear explanations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bret Fisher&lt;/td&gt;
&lt;td&gt;YouTube / Course&lt;/td&gt;
&lt;td&gt;&lt;code&gt;youtube.com/@BretFisher&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Deep DevOps focus, Docker Swarm, real-world production tips&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;KodeKloud&lt;/td&gt;
&lt;td&gt;Course Platform&lt;/td&gt;
&lt;td&gt;&lt;code&gt;kodekloud.com&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Hands-on browser labs, great for beginners, Kubernetes learning path&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Docker Deep Dive (Nigel Poulton)&lt;/td&gt;
&lt;td&gt;Book&lt;/td&gt;
&lt;td&gt;Amazon / Leanpub&lt;/td&gt;
&lt;td&gt;Best Docker book — concise, practical, updated regularly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;awesome-docker&lt;/td&gt;
&lt;td&gt;GitHub List&lt;/td&gt;
&lt;td&gt;&lt;code&gt;github.com/veggiemonk/awesome-docker&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Curated list of Docker tools, tutorials, and resources&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Docker Official Blog&lt;/td&gt;
&lt;td&gt;Blog&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker.com/blog&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Latest Docker updates, best practices, case studies&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ivan Velichko's Blog&lt;/td&gt;
&lt;td&gt;Blog&lt;/td&gt;
&lt;td&gt;&lt;code&gt;iximiuz.com&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Deep internals: containers, namespaces, cgroups explained with clarity&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  4-Week Practice Plan
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Week 1: Install Docker, run official images (nginx, postgres, redis, ubuntu)
        → docker run, docker ps, docker logs, docker exec
        → Understand port mapping, volume mounting, environment variables

Week 2: Write Dockerfiles for your existing React/Node projects
        → .dockerignore, layer caching, multi-stage builds
        → Optimize: get below 200 MB for your application image

Week 3: Docker Compose — build a full MERN/PERN stack locally
        → depends_on, healthchecks, named volumes
        → Development vs production compose files, profiles

Week 4: Security, multi-stage builds, push to ECR, deploy to ECS/Fargate
        → Non-root USER, trivy scan, hadolint check
        → GitHub Actions pipeline: build → push → deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Certification Paths
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Docker Certified Associate (DCA)&lt;/strong&gt;&lt;br&gt;
Official Docker certification covering installation, configuration, networking, security, and orchestration. A solid credential for validating foundational knowledge.&lt;br&gt;
Format: 55 MCQ, 90 minutes | Cost: approximately $195 USD&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CKA — Certified Kubernetes Administrator&lt;/strong&gt;&lt;br&gt;
The natural next step after mastering Docker. Kubernetes is Docker at scale, and your Docker knowledge maps directly to Kubernetes concepts.&lt;br&gt;
Format: Hands-on practical exam, 2 hours | Cost: approximately $395 USD&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Solutions Architect&lt;/strong&gt;&lt;br&gt;
Includes deep coverage of ECS, Fargate, ECR, and EKS. Highly valued in the job market for senior DevOps and cloud engineering roles.&lt;br&gt;
Format: 65 MCQ, 130 minutes | Cost: approximately $150 USD&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Next? Your Docker Mastery Roadmap
&lt;/h2&gt;

&lt;p&gt;You now have the knowledge — the next step is execution. Here is a precise 30-day plan to go from Docker learner to production-ready DevOps engineer.&lt;/p&gt;

&lt;h3&gt;
  
  
  30-Day Docker Mastery Plan
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Days&lt;/th&gt;
&lt;th&gt;Topic&lt;/th&gt;
&lt;th&gt;Task / Goal&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1–2&lt;/td&gt;
&lt;td&gt;Installation and First Steps&lt;/td&gt;
&lt;td&gt;Install Docker on Ubuntu, run hello-world, nginx, postgres — understand what each does&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3–5&lt;/td&gt;
&lt;td&gt;Container Commands&lt;/td&gt;
&lt;td&gt;Practice all docker run flags, docker logs, exec, inspect, stats — build muscle memory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6–8&lt;/td&gt;
&lt;td&gt;Images Deep Dive&lt;/td&gt;
&lt;td&gt;Pull images, run docker history, inspect layers with dive, understand image size&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9–12&lt;/td&gt;
&lt;td&gt;Dockerfile Mastery&lt;/td&gt;
&lt;td&gt;Write Dockerfiles for React, Node.js, and Python apps from scratch&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;13–15&lt;/td&gt;
&lt;td&gt;Multi-Stage Builds&lt;/td&gt;
&lt;td&gt;Optimize your apps: push image size from 1 GB down to 100 MB or less&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;16–18&lt;/td&gt;
&lt;td&gt;Networking&lt;/td&gt;
&lt;td&gt;Create custom networks, wire up MERN stack containers, test DNS resolution&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;19–21&lt;/td&gt;
&lt;td&gt;Volumes and Persistence&lt;/td&gt;
&lt;td&gt;Database persistence across restarts, dev bind mounts, tmpfs for secrets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;22–25&lt;/td&gt;
&lt;td&gt;Docker Compose&lt;/td&gt;
&lt;td&gt;Full stack with Compose: health checks, depends_on, dev/prod override files, profiles&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;26–27&lt;/td&gt;
&lt;td&gt;Security Hardening&lt;/td&gt;
&lt;td&gt;Non-root USER, trivy scan (zero HIGH/CRITICAL), hadolint (zero warnings), secrets management&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;28–29&lt;/td&gt;
&lt;td&gt;CI/CD Pipeline&lt;/td&gt;
&lt;td&gt;GitHub Actions: auto-build on push → push to Docker Hub or ECR → deploy to server&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;30&lt;/td&gt;
&lt;td&gt;AWS Deployment&lt;/td&gt;
&lt;td&gt;Push to ECR, create ECS task definition, deploy to Fargate — live in production&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Docker to Kubernetes Transition Map
&lt;/h3&gt;

&lt;p&gt;Every Docker concept has a direct Kubernetes equivalent. Your Docker knowledge is your Kubernetes head start.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Docker Concept&lt;/th&gt;
&lt;th&gt;Kubernetes Equivalent&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Container&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Pod&lt;/code&gt; (wraps one or more containers)&lt;/td&gt;
&lt;td&gt;Pods are the smallest deployable unit in Kubernetes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker run&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;kubectl run&lt;/code&gt; / Deployment&lt;/td&gt;
&lt;td&gt;Deployments add replicas, rollouts, rollbacks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker-compose.yml&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Deployment + Service YAML&lt;/td&gt;
&lt;td&gt;Compose maps to multiple Kubernetes manifest files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Docker network&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Service + NetworkPolicy&lt;/td&gt;
&lt;td&gt;Kubernetes Services provide stable DNS endpoints for pods&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Docker volume&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;PersistentVolume (PV/PVC)&lt;/td&gt;
&lt;td&gt;PVCs allow dynamic storage provisioning&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker compose up&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;kubectl apply -f&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Declarative — Kubernetes reconciles desired state&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Docker Swarm service&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Deployment with replicas&lt;/td&gt;
&lt;td&gt;Kubernetes has much more sophisticated scheduling&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;HEALTHCHECK&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;readinessProbe + livenessProbe&lt;/td&gt;
&lt;td&gt;Kubernetes has separate readiness, liveness, and startup probes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker secret&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Kubernetes Secret&lt;/td&gt;
&lt;td&gt;Kubernetes Secrets can be mounted as files or environment variables&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--env-file&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;ConfigMap + Secret&lt;/td&gt;
&lt;td&gt;ConfigMaps for non-sensitive config, Secrets for sensitive values&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Need to plan resource requests for Kubernetes? Use the &lt;a href="https://opscanopy.com/kubernetes-resource-calculator/" rel="noopener noreferrer"&gt;Kubernetes Resource Calculator&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Portfolio Projects for Senior DevOps Roles
&lt;/h3&gt;

&lt;p&gt;Build these five projects to make your GitHub profile stand out to technical recruiters:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Dockerized MERN Stack&lt;/strong&gt; — Full MERN (MongoDB, Express, React, Node) stack with Docker Compose. Proper health checks, named volumes, multi-stage build for React. Include a detailed README with an architecture diagram.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;CI/CD Pipeline&lt;/strong&gt; — GitHub Actions workflow that automatically builds an image on push to main, runs tests, pushes to ECR with a version tag, and deploys to ECS/Fargate. Include a rollback workflow.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Microservices Application&lt;/strong&gt; — Three or more services (auth, products, orders) each in separate containers with an API gateway. Service discovery via Docker DNS, isolated databases, event-driven communication.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Security-Hardened Image&lt;/strong&gt; — Dockerfile that passes a trivy scan with zero HIGH/CRITICAL CVEs, passes hadolint with zero warnings, runs as a non-root user, and uses multi-stage builds. Include scan reports in the README.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Monitoring Stack&lt;/strong&gt; — Application with Prometheus, Grafana, and Alertmanager in Docker Compose. Custom dashboards showing container metrics, application metrics, and alerting rules.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Moving Forward
&lt;/h3&gt;

&lt;p&gt;Completing this guide means you can confidently write production-grade Dockerfiles, deploy multi-container applications with Compose, use AWS ECR and ECS with Fargate, follow Docker security best practices, and tackle DevOps interviews with confidence.&lt;/p&gt;

&lt;p&gt;Your Docker knowledge is the foundation for everything in modern DevOps. The concepts of containers, immutable images, service isolation, and declarative configuration carry directly into the Kubernetes ecosystem.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Prefer a day-by-day path? This is covered in &lt;a href="https://opscanopy.com/mission-90/" rel="noopener noreferrer"&gt;&lt;strong&gt;Mission 90 Days 22–30&lt;/strong&gt;&lt;/a&gt; — a free 90-day guided DevOps program with browser terminal missions.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Ready to orchestrate at scale? Continue with &lt;a href="https://opscanopy.com/learn/guides/kubernetes-for-devops/" rel="noopener noreferrer"&gt;Kubernetes for DevOps&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>containers</category>
      <category>devops</category>
      <category>dockerfile</category>
    </item>
    <item>
      <title>AWS for DevOps Engineers: The Core Services You Actually Need</title>
      <dc:creator>opscanopy</dc:creator>
      <pubDate>Sat, 15 Aug 2026 13:45:34 +0000</pubDate>
      <link>https://dev.to/opscanopy/aws-for-devops-engineers-the-core-services-you-actually-need-59a5</link>
      <guid>https://dev.to/opscanopy/aws-for-devops-engineers-the-core-services-you-actually-need-59a5</guid>
      <description>&lt;p&gt;A focused, interview-ready guide built for a developer moving into DevOps. Depth on the services that actually show up in DevOps work and interviews — not all 200+ AWS offerings. This guide covers Regions and AZs, IAM, EC2, Lambda, containers, VPC networking, S3/EBS/EFS storage, RDS and DynamoDB, the AWS CLI, and cost and security guardrails. See the &lt;a href="https://opscanopy.com/learn/roadmaps/aws/" rel="noopener noreferrer"&gt;AWS roadmap&lt;/a&gt; for a structured learning path through certifications and deeper specialisations.&lt;/p&gt;

&lt;h2&gt;
  
  
  AWS Fundamentals
&lt;/h2&gt;

&lt;p&gt;Before you touch a single EC2 instance, you need the mental map: &lt;strong&gt;where&lt;/strong&gt; your stuff runs (Regions &amp;amp; AZs), &lt;strong&gt;who is responsible&lt;/strong&gt; for what (Shared Responsibility), and &lt;strong&gt;how you pay&lt;/strong&gt; for it (Billing &amp;amp; Free Tier). Get these three right and you avoid 90% of the surprises beginners hit.&lt;/p&gt;

&lt;h3&gt;
  
  
  Regions and Availability Zones
&lt;/h3&gt;

&lt;p&gt;AWS physically runs in &lt;strong&gt;Regions&lt;/strong&gt; (e.g. &lt;code&gt;us-east-1&lt;/code&gt; = N. Virginia, &lt;code&gt;eu-west-1&lt;/code&gt; = Ireland, &lt;code&gt;ap-southeast-1&lt;/code&gt; = Singapore). Each Region is a cluster of isolated datacenters grouped into &lt;strong&gt;Availability Zones (AZs)&lt;/strong&gt;. An AZ is one or more physical datacenters with independent power, cooling, and networking, but AZs in the same Region are linked by low-latency private fiber.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Region&lt;/strong&gt; = a city (N. Virginia). A geographic area you pick.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AZ&lt;/strong&gt; = separate neighborhoods in that city (&lt;code&gt;us-east-1a&lt;/code&gt;, &lt;code&gt;us-east-1b&lt;/code&gt;, &lt;code&gt;us-east-1c&lt;/code&gt;), each with its own power grid, far enough apart that one flood/fire won't take down another.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Edge Location&lt;/strong&gt; = a corner shop close to your house — used by CloudFront (CDN) and Route 53 to cache content near users for speed.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Think of a Region like a big housing society in one city. Each AZ is a separate building (Tower A, B, C) with its &lt;em&gt;own&lt;/em&gt; generator and water tank. If Tower A's power trips, Tower B is fine. So you spread your app across multiple towers — that's &lt;strong&gt;Multi-AZ&lt;/strong&gt;. Edge Locations are the ATMs scattered all over the city so you don't have to drive to the main bank for cash.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Why Multi-AZ Matters for High Availability
&lt;/h4&gt;

&lt;p&gt;If you run everything in one AZ and that AZ goes down, your app goes down. Period. Running across 2+ AZs means a failure in one is automatically absorbed by the others.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Multi-AZ RDS&lt;/strong&gt;: a standby DB copy sits in another AZ; on failure AWS auto-promotes it (failover in ~60–120s).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;EC2 + ELB Auto Scaling&lt;/strong&gt;: spread instances across AZs; the load balancer routes around the dead AZ.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-Region&lt;/strong&gt; is the next level up — for disaster recovery (DR) and global latency, not just AZ failure. It costs more and is harder (data replication, DNS failover).&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; A fintech runs its API on EC2 across &lt;code&gt;us-east-1a&lt;/code&gt; and &lt;code&gt;us-east-1b&lt;/code&gt; behind an Application Load Balancer, with RDS in Multi-AZ mode. When AWS had an AZ-level network issue, the ALB stopped sending traffic to the unhealthy AZ and RDS failed over to the standby. End users saw a 90-second blip instead of an outage. Single-AZ would have meant hours of downtime.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  How to Choose a Region
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Factor&lt;/th&gt;
&lt;th&gt;What to check&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Latency&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Pick the Region closest to your &lt;em&gt;users&lt;/em&gt;. Users in Southeast Asia → &lt;code&gt;ap-southeast-1&lt;/code&gt;. Test with cloudping or RTT measurements.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cost&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Prices differ per Region. &lt;code&gt;us-east-1&lt;/code&gt; is usually cheapest; some Regions (e.g. Sydney, São Paulo) can be pricier. Same instance, different bill.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Compliance / Data residency&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Customer/financial data may legally need to stay in-country → pick the matching Region. GDPR data → an EU Region like &lt;code&gt;eu-west-1&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Service availability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Newest services launch in &lt;code&gt;us-east-1&lt;/code&gt; first. Some Regions lag. Check before committing.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Some services are &lt;strong&gt;global&lt;/strong&gt;, not regional: IAM, Route 53, CloudFront, and WAF (for CloudFront). But the AWS console quietly defaults a lot of regional resources to &lt;code&gt;us-east-1&lt;/code&gt; — always confirm your selected Region (top-right of the console) before creating resources, or you'll "lose" an EC2 instance that's actually running (and billing) in the wrong Region.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Region vs AZ vs Edge Location
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Region&lt;/th&gt;
&lt;th&gt;Availability Zone&lt;/th&gt;
&lt;th&gt;Edge Location&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;What it is&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Geographic area (a city)&lt;/td&gt;
&lt;td&gt;1+ isolated datacenters in a Region&lt;/td&gt;
&lt;td&gt;Small CDN/cache PoP near users&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Count&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;30+ globally&lt;/td&gt;
&lt;td&gt;Usually 3+ per Region&lt;/td&gt;
&lt;td&gt;400+ globally&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Used for&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Choosing where to deploy&lt;/td&gt;
&lt;td&gt;HA &amp;amp; fault tolerance within a Region&lt;/td&gt;
&lt;td&gt;Caching content close to users (low latency)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Example service&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Picked when launching EC2/RDS&lt;/td&gt;
&lt;td&gt;Multi-AZ RDS, Auto Scaling groups&lt;/td&gt;
&lt;td&gt;CloudFront, Route 53, Global Accelerator&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Isolation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Fully isolated from other Regions&lt;/td&gt;
&lt;td&gt;Independent power/cooling/network&lt;/td&gt;
&lt;td&gt;Not for compute — caching only&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Classic question: "What's the difference between Multi-AZ and Multi-Region?" Answer crisply: &lt;strong&gt;Multi-AZ = High Availability&lt;/strong&gt; within one Region (handles datacenter/AZ failure, automatic, low cost). &lt;strong&gt;Multi-Region = Disaster Recovery + global reach&lt;/strong&gt; across Regions (handles a whole-Region failure, more complex, costlier, needs data replication and DNS failover). Don't conflate them.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Shared Responsibility Model
&lt;/h3&gt;

&lt;p&gt;AWS does NOT secure everything for you. Security and compliance are &lt;strong&gt;shared&lt;/strong&gt;. The line is simple to remember:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AWS is responsible for the security OF the cloud&lt;/strong&gt; — the hardware, the physical datacenters, the host hypervisor, networking backbone, and the managed-service infrastructure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You are responsible for security IN the cloud&lt;/strong&gt; — your data, your OS patches (on EC2), your IAM users/roles, your security groups, your application code, encryption settings.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; AWS is like a rental apartment building. The landlord (AWS) handles the building structure, the main gate security, the foundation, electricity supply. But &lt;em&gt;inside your flat&lt;/em&gt; — locking your own door, what you keep inside, who you give a duplicate key to (IAM keys!) — that's on you. If you leave your flat door wide open, you can't blame the landlord.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;AWS — "OF the cloud"&lt;/th&gt;
&lt;th&gt;You (DevOps) — "IN the cloud"&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Physical datacenter &amp;amp; hardware security&lt;/td&gt;
&lt;td&gt;IAM users, roles, groups, MFA, least privilege&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hypervisor &amp;amp; host OS patching&lt;/td&gt;
&lt;td&gt;Guest OS patching on EC2 (you own the AMI/instance)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Global network infrastructure&lt;/td&gt;
&lt;td&gt;Security Groups, NACLs, VPC subnet design&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Managed service uptime (S3, RDS engine, Lambda runtime)&lt;/td&gt;
&lt;td&gt;Your data, client/server-side encryption, S3 bucket policies (don't make it public by accident!)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Decommissioning/wiping failed disks&lt;/td&gt;
&lt;td&gt;Your application code &amp;amp; its vulnerabilities&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; The responsibility line &lt;em&gt;shifts&lt;/em&gt; with the service type. On &lt;strong&gt;EC2 (IaaS)&lt;/strong&gt; you patch the OS. On &lt;strong&gt;RDS (PaaS)&lt;/strong&gt; AWS patches the DB engine but you still manage users, schemas, and encryption. On &lt;strong&gt;Lambda/S3 (serverless/managed)&lt;/strong&gt; AWS handles even more, but data security and access policies are &lt;em&gt;always&lt;/em&gt; yours. The most common breach in the news? A publicly-exposed S3 bucket — 100% the customer's fault, never AWS's.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Billing and Free Tier
&lt;/h3&gt;

&lt;p&gt;AWS pricing is fundamentally &lt;strong&gt;pay-as-you-go&lt;/strong&gt;: no upfront cost (unless you choose to commit), you pay only for what you actually use — per second/hour of compute, per GB of storage, per GB of data transfer out. No usage = (mostly) no bill. But "mostly" is where beginners get bitten.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Pay-as-you-go is like an electricity meter, not a fixed monthly rent. You only pay for the units you burn. But some things keep the meter running even when you're "not using" them — like a reserved parking spot you booked but left empty (an idle Elastic IP), or a security guard you hired and forgot to send home (a running NAT Gateway). The meter doesn't care that you forgot.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  The 3 Types of Free Tier
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;How long&lt;/th&gt;
&lt;th&gt;Examples&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Always Free&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Forever (within limits)&lt;/td&gt;
&lt;td&gt;Lambda 1M requests/month, DynamoDB 25 GB, CloudWatch basic metrics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;12-Month Free&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;12 months from signup&lt;/td&gt;
&lt;td&gt;EC2 &lt;code&gt;t2.micro&lt;/code&gt;/&lt;code&gt;t3.micro&lt;/code&gt; 750 hrs/mo, S3 5 GB, RDS 750 hrs/mo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Trials&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Short fixed window (e.g. 30/90 days)&lt;/td&gt;
&lt;td&gt;Short-term service trials like certain SageMaker/Inspector features&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; What surprises beginners and quietly inflates the bill — even on "free tier": &lt;strong&gt;1) Data transfer OUT:&lt;/strong&gt; data going INTO AWS is free, but data going OUT to the internet costs money beyond the small free allowance. &lt;strong&gt;2) Idle Elastic IPs (EIPs):&lt;/strong&gt; an EIP is free &lt;em&gt;only while attached to a running instance&lt;/em&gt;. Allocate one and leave it unattached and you pay hourly. &lt;strong&gt;3) NAT Gateway:&lt;/strong&gt; charges both an hourly rate AND per-GB processed — and it is NOT free tier. Spin one up in a tutorial, forget it, and it bleeds money 24/7. This is the #1 "why is my bill so high?" culprit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; On day one: set a &lt;strong&gt;Billing Alarm&lt;/strong&gt; in CloudWatch (or AWS Budgets) for a low threshold like $5–$10. Enable &lt;strong&gt;Cost Explorer&lt;/strong&gt; and check it weekly. Also stop/terminate tutorial resources when done — &lt;code&gt;stop&lt;/code&gt; halts EC2 compute charges but you still pay for the EBS volume; &lt;code&gt;terminate&lt;/code&gt; kills it fully. Tag resources by project so you can see exactly what's costing what.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  IAM — Identity and Access Management
&lt;/h2&gt;

&lt;p&gt;IAM is the &lt;strong&gt;front gate and key system&lt;/strong&gt; of your entire AWS account: who can log in, and what they're allowed to do. It's the #1 DevOps interview area because almost every security incident traces back to bad IAM. IAM is &lt;strong&gt;global&lt;/strong&gt; (not regional) and, importantly, &lt;strong&gt;free&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;/p&gt;
An IAM user or an EC2 instance role both attach to a policy, which defines which AWS resources and actions are permitted.





&lt;h3&gt;
  
  
  Root User vs IAM User
&lt;/h3&gt;

&lt;p&gt;When you create an AWS account, you get a &lt;strong&gt;root user&lt;/strong&gt; (the email you signed up with). The root user has unlimited, unrestricted power — it can do &lt;em&gt;anything&lt;/em&gt;, including close the account and change billing. That's exactly why you should almost never use it.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Root user&lt;/strong&gt;: full god-mode access. Use it only for a tiny set of tasks that &lt;em&gt;require&lt;/em&gt; root (e.g. changing account settings, closing the account).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IAM user&lt;/strong&gt;: an identity you create for a person or app, with &lt;em&gt;only&lt;/em&gt; the permissions they need.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The root user is the &lt;strong&gt;master key&lt;/strong&gt; to the whole building — opens every door, the safe, the main switchboard, everything. You don't carry the master key around daily; you lock it in a vault, put a heavy alarm (MFA) on it, and hand out individual room keys (IAM users) for day-to-day work. If you lose the master key, the whole building is compromised.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; Lock away root immediately: (1) enable &lt;strong&gt;MFA on root&lt;/strong&gt;, (2) delete any root &lt;strong&gt;access keys&lt;/strong&gt; — root should never have programmatic keys, (3) create an admin IAM user (or, better, use IAM Identity Center / SSO) for daily work, (4) never share or hardcode root credentials. A leaked root key = total account takeover, including running up massive bills and deleting all your data.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Users vs Groups
&lt;/h3&gt;

&lt;p&gt;Attaching policies to each user individually doesn't scale. &lt;strong&gt;Groups&lt;/strong&gt; are collections of users — you attach policies to the group, and every member inherits them.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;User&lt;/strong&gt;: a single identity (a person or a service account).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Group&lt;/strong&gt;: e.g. &lt;code&gt;Developers&lt;/code&gt;, &lt;code&gt;Admins&lt;/code&gt;, &lt;code&gt;ReadOnly&lt;/code&gt;. Add a user to the group and they get its permissions. Remove them and access goes away instantly.&lt;/li&gt;
&lt;li&gt;A group is NOT an identity — you can't make a group the "principal" of an action, and groups can't be nested.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Policies — The Rules of Access
&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;policy&lt;/strong&gt; is a JSON document defining permissions. Two big ways to categorize them:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Identity-based policy&lt;/th&gt;
&lt;th&gt;Resource-based policy&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Attached to&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;An IAM user, group, or role&lt;/td&gt;
&lt;td&gt;A resource (e.g. S3 bucket, SQS queue, KMS key)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Answers&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;"What can this identity do?"&lt;/td&gt;
&lt;td&gt;"Who can access this resource?"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Has a Principal?&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No (the identity is the principal)&lt;/td&gt;
&lt;td&gt;Yes — specifies &lt;em&gt;who&lt;/em&gt; (account/user/service)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cross-account&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Not directly&lt;/td&gt;
&lt;td&gt;Yes — great for sharing across accounts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Example&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;"Devs can read S3 bucket X"&lt;/td&gt;
&lt;td&gt;"S3 bucket policy: allow account B to read"&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Managed vs inline:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AWS Managed&lt;/strong&gt;: prebuilt by AWS (e.g. &lt;code&gt;AmazonS3ReadOnlyAccess&lt;/code&gt;). Convenient, auto-updated, but often broader than you need.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Customer Managed&lt;/strong&gt;: you create and reuse them across identities. Best for least privilege — version-controlled, reusable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inline&lt;/strong&gt;: embedded directly into one user/group/role, 1:1, deleted with it. Use sparingly — for strict one-off relationships only.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Policy JSON Structure
&lt;/h4&gt;

&lt;p&gt;Every statement has an &lt;strong&gt;Effect&lt;/strong&gt; (Allow/Deny), &lt;strong&gt;Action&lt;/strong&gt; (the API calls), &lt;strong&gt;Resource&lt;/strong&gt; (the ARN it applies to), and optionally a &lt;strong&gt;Condition&lt;/strong&gt;. This one lets a user list a specific bucket and read/write objects only under the &lt;code&gt;app/&lt;/code&gt; prefix:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"Version"&lt;/span&gt;: &lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;,
  &lt;span class="s2"&gt;"Statement"&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;
    &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="s2"&gt;"Sid"&lt;/span&gt;: &lt;span class="s2"&gt;"ListTheBucket"&lt;/span&gt;,
      &lt;span class="s2"&gt;"Effect"&lt;/span&gt;: &lt;span class="s2"&gt;"Allow"&lt;/span&gt;,
      &lt;span class="s2"&gt;"Action"&lt;/span&gt;: &lt;span class="s2"&gt;"s3:ListBucket"&lt;/span&gt;,
      &lt;span class="s2"&gt;"Resource"&lt;/span&gt;: &lt;span class="s2"&gt;"arn:aws:s3:::my-app-bucket"&lt;/span&gt;,
      &lt;span class="s2"&gt;"Condition"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;"StringLike"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"s3:prefix"&lt;/span&gt;: &lt;span class="s2"&gt;"app/*"&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
      &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;,
    &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="s2"&gt;"Sid"&lt;/span&gt;: &lt;span class="s2"&gt;"ReadWriteAppObjects"&lt;/span&gt;,
      &lt;span class="s2"&gt;"Effect"&lt;/span&gt;: &lt;span class="s2"&gt;"Allow"&lt;/span&gt;,
      &lt;span class="s2"&gt;"Action"&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;
        &lt;span class="s2"&gt;"s3:GetObject"&lt;/span&gt;,
        &lt;span class="s2"&gt;"s3:PutObject"&lt;/span&gt;
      &lt;span class="o"&gt;]&lt;/span&gt;,
      &lt;span class="s2"&gt;"Resource"&lt;/span&gt;: &lt;span class="s2"&gt;"arn:aws:s3:::my-app-bucket/app/*"&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Remember the evaluation logic: &lt;strong&gt;everything is implicitly denied by default&lt;/strong&gt;. An explicit &lt;code&gt;Allow&lt;/code&gt; grants access, but an explicit &lt;code&gt;Deny&lt;/code&gt; &lt;em&gt;always wins&lt;/em&gt; over any Allow. So you can broadly allow, then surgically deny sensitive actions. Interviewers love asking "Allow vs Deny — which wins?" Answer: &lt;strong&gt;explicit Deny always trumps Allow.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  IAM Roles — Why Roles Beat Long-Lived Keys
&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;role&lt;/strong&gt; is an identity with permissions but &lt;strong&gt;no permanent credentials&lt;/strong&gt;. Instead, a trusted entity &lt;em&gt;assumes&lt;/em&gt; the role and gets &lt;strong&gt;temporary credentials&lt;/strong&gt; (via STS) that auto-expire. This is the single most important IAM best practice.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; An access key is like a permanent house key you cut and hand out — if it leaks, it works forever until someone notices and changes the lock. A role is like a &lt;strong&gt;hotel keycard&lt;/strong&gt;: the front desk (STS) issues a fresh card that stops working after checkout. Even if someone copies it, it's useless in a few hours. This is why roles always beat long-lived keys.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Where roles shine:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;EC2 Instance Profile&lt;/strong&gt;: attach a role to an EC2 instance so your app calls AWS APIs without ever storing keys on the box. The SDK auto-fetches rotating temp creds from instance metadata.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AssumeRole&lt;/strong&gt;: a user/service calls &lt;code&gt;sts:AssumeRole&lt;/code&gt; to temporarily become the role.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cross-account access&lt;/strong&gt;: account A's role trusts account B; B's users assume it — no shared keys between accounts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Service roles&lt;/strong&gt;: e.g. Lambda execution role, ECS task role — the AWS service assumes the role to act on your behalf.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; Never hardcode access keys in code, Dockerfiles, environment files committed to Git, or AMIs. Leaked keys on public GitHub are scraped by bots within &lt;em&gt;minutes&lt;/em&gt; and used to spin up crypto-mining fleets — landing you a five-figure bill. On EC2/ECS/Lambda, always use a role instead.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  IAM User vs IAM Role
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;IAM User&lt;/th&gt;
&lt;th&gt;IAM Role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Credentials&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Long-lived (password / access keys)&lt;/td&gt;
&lt;td&gt;Temporary, auto-expiring (via STS)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Who uses it&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A specific person/app, persistently&lt;/td&gt;
&lt;td&gt;Anyone/anything trusted who assumes it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Best for&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Human console login&lt;/td&gt;
&lt;td&gt;EC2/Lambda/ECS, cross-account, federation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Rotation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Manual, easy to forget&lt;/td&gt;
&lt;td&gt;Automatic — handled by AWS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Leak risk&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;High (keys live forever)&lt;/td&gt;
&lt;td&gt;Low (creds expire in minutes/hours)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  IAM CLI Examples
&lt;/h3&gt;

&lt;p&gt;Create a user, put them in a group, attach a managed policy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create an IAM user&lt;/span&gt;
aws iam create-user &lt;span class="nt"&gt;--user-name&lt;/span&gt; riya-dev

&lt;span class="c"&gt;# Create a group and attach an AWS managed policy to it&lt;/span&gt;
aws iam create-group &lt;span class="nt"&gt;--group-name&lt;/span&gt; Developers
aws iam attach-group-policy &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--group-name&lt;/span&gt; Developers &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--policy-arn&lt;/span&gt; arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess

&lt;span class="c"&gt;# Add the user to the group (inherits the group's permissions)&lt;/span&gt;
aws iam add-user-to-group &lt;span class="nt"&gt;--user-name&lt;/span&gt; riya-dev &lt;span class="nt"&gt;--group-name&lt;/span&gt; Developers

&lt;span class="c"&gt;# Enforce MFA-friendly console access (set a login profile / password)&lt;/span&gt;
aws iam create-login-profile &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--user-name&lt;/span&gt; riya-dev &lt;span class="nt"&gt;--password&lt;/span&gt; &lt;span class="s1"&gt;'Temp#Pass123'&lt;/span&gt; &lt;span class="nt"&gt;--password-reset-required&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a &lt;strong&gt;role&lt;/strong&gt; with a trust policy so EC2 can assume it, then give the role permissions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1) Trust policy: WHO is allowed to assume this role (here: the EC2 service)&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; trust-policy.json &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": { "Service": "ec2.amazonaws.com" },
      "Action": "sts:AssumeRole"
    }
  ]
}
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="c"&gt;# 2) Create the role with that trust policy&lt;/span&gt;
aws iam create-role &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--role-name&lt;/span&gt; app-s3-role &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--assume-role-policy-document&lt;/span&gt; file://trust-policy.json

&lt;span class="c"&gt;# 3) Attach a permissions policy (WHAT the role can do)&lt;/span&gt;
aws iam attach-role-policy &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--role-name&lt;/span&gt; app-s3-role &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--policy-arn&lt;/span&gt; arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess

&lt;span class="c"&gt;# 4) Wrap it in an instance profile and attach to EC2 (no keys needed!)&lt;/span&gt;
aws iam create-instance-profile &lt;span class="nt"&gt;--instance-profile-name&lt;/span&gt; app-s3-profile
aws iam add-role-to-instance-profile &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--instance-profile-name&lt;/span&gt; app-s3-profile &lt;span class="nt"&gt;--role-name&lt;/span&gt; app-s3-role
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Two policy types per role: the &lt;strong&gt;trust policy&lt;/strong&gt; (the &lt;code&gt;assume-role-policy-document&lt;/code&gt;) controls &lt;em&gt;WHO can assume&lt;/em&gt; the role, and the &lt;strong&gt;permissions policy&lt;/strong&gt; controls &lt;em&gt;WHAT&lt;/em&gt; the role can do once assumed. Interviewers often probe this distinction — keep them separate in your head.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Least Privilege
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Grant only the minimum permissions needed to do the job — nothing more.&lt;/strong&gt; Start with zero, add permissions as required, and scope tightly (specific actions, specific resource ARNs, conditions).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Prefer specific actions (&lt;code&gt;s3:GetObject&lt;/code&gt;) over wildcards (&lt;code&gt;s3:*&lt;/code&gt; or &lt;code&gt;*&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Scope &lt;code&gt;Resource&lt;/code&gt; to exact ARNs, not &lt;code&gt;"*"&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;IAM Access Analyzer&lt;/strong&gt; to generate least-privilege policies from real CloudTrail usage.&lt;/li&gt;
&lt;li&gt;Review with the &lt;strong&gt;IAM Policy Simulator&lt;/strong&gt; before rollout. Audit with Access Analyzer / credential reports regularly.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  MFA and Access Key Best Practices
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;MFA everywhere&lt;/strong&gt;: mandatory on root and all human users. Add a virtual/hardware MFA device. You can even &lt;em&gt;require&lt;/em&gt; MFA in policies via a &lt;code&gt;Condition&lt;/code&gt; (&lt;code&gt;aws:MultiFactorAuthPresent&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Access keys&lt;/strong&gt;: only for programmatic CLI/SDK use where roles aren't possible. Rotate them regularly, never commit to Git, and delete unused keys.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prefer roles&lt;/strong&gt; over access keys whenever the workload runs inside AWS (EC2/ECS/Lambda) — zero keys to manage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Audit&lt;/strong&gt;: use IAM credential reports and CloudTrail to find stale keys and unused permissions.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Compute — EC2, Lambda, Containers
&lt;/h2&gt;

&lt;p&gt;Compute is "where your code actually runs." On AWS you pick how much control you want versus how much AWS manages for you. More control = more responsibility (EC2). Less control = AWS handles the boring stuff (Lambda, Fargate). As a DevOps engineer your job is choosing the right one for the workload.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;br&gt;
  &lt;/p&gt;
EC2 gives full OS control for long-running workloads; Lambda is event-driven and fully managed, scaling to zero between invocations.





&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Think of renting a place. &lt;strong&gt;EC2&lt;/strong&gt; is renting an empty flat — you bring furniture, fix the plumbing, control everything. &lt;strong&gt;Lambda&lt;/strong&gt; is a hotel room you only pay for the minutes you're inside. &lt;strong&gt;Fargate&lt;/strong&gt; is a serviced apartment — your stuff, but housekeeping (servers) is handled. Same "shelter," very different effort.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  EC2 — Elastic Compute Cloud
&lt;/h3&gt;

&lt;p&gt;EC2 is a virtual server (a VM) in AWS's data center. You choose the OS, CPU, RAM, disk, and you SSH in like any Linux box. It's the foundational "rent a server" service.&lt;/p&gt;

&lt;h4&gt;
  
  
  Instance Families and Types
&lt;/h4&gt;

&lt;p&gt;Instances are grouped into &lt;strong&gt;families&lt;/strong&gt; optimized for different workloads. The naming follows a pattern: &lt;code&gt;m5.large&lt;/code&gt; = family &lt;code&gt;m&lt;/code&gt;, generation &lt;code&gt;5&lt;/code&gt;, size &lt;code&gt;large&lt;/code&gt;. Bigger size = more vCPU + RAM (and roughly double the price each step up).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;General purpose (t, m)&lt;/strong&gt; — balanced CPU/RAM. &lt;code&gt;t3.micro&lt;/code&gt; (burstable, cheap, great for dev/small web), &lt;code&gt;m5.large&lt;/code&gt; (steady workloads, app servers).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compute optimized (c)&lt;/strong&gt; — high CPU per RAM. &lt;code&gt;c5.xlarge&lt;/code&gt; for batch processing, gaming servers, CI build runners.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory optimized (r, x)&lt;/strong&gt; — lots of RAM. &lt;code&gt;r5.large&lt;/code&gt; for databases, in-memory caches (Redis), big data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Storage optimized (i, d)&lt;/strong&gt; — high disk throughput/IOPS. &lt;code&gt;i3.large&lt;/code&gt; for NoSQL, data warehousing, large local datasets.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; &lt;code&gt;t&lt;/code&gt;-family instances are &lt;em&gt;burstable&lt;/em&gt; — they earn CPU credits when idle and spend them during spikes. Perfect for spiky low-traffic apps, but a constantly-busy server will run out of credits and throttle. For steady load, use &lt;code&gt;m&lt;/code&gt;/&lt;code&gt;c&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  AMIs (Amazon Machine Images)
&lt;/h4&gt;

&lt;p&gt;An AMI is the template an instance boots from — OS + pre-installed software + config. Think of it as a snapshot/golden image. You launch from an AWS-provided AMI (Amazon Linux, Ubuntu) or bake your own with your app baked in (faster, repeatable deploys — a core DevOps practice with tools like Packer).&lt;/p&gt;

&lt;h4&gt;
  
  
  Key Pairs (SSH)
&lt;/h4&gt;

&lt;p&gt;To log into a Linux EC2 instance you use SSH key-based auth — no passwords. AWS gives you the &lt;strong&gt;public&lt;/strong&gt; key (placed on the instance), you keep the &lt;strong&gt;private&lt;/strong&gt; &lt;code&gt;.pem&lt;/code&gt; file. Lose the private key and you're locked out.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create a key pair and save the private key locally&lt;/span&gt;
aws ec2 create-key-pair &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--key-name&lt;/span&gt; my-devops-key &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s1"&gt;'KeyMaterial'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; text &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; my-devops-key.pem

&lt;span class="c"&gt;# Lock down permissions (SSH refuses world-readable keys)&lt;/span&gt;
&lt;span class="nb"&gt;chmod &lt;/span&gt;400 my-devops-key.pem

&lt;span class="c"&gt;# Later, connect to the instance&lt;/span&gt;
ssh &lt;span class="nt"&gt;-i&lt;/span&gt; my-devops-key.pem ec2-user@&amp;lt;PUBLIC_IP&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  User Data (Bootstrap Script)
&lt;/h4&gt;

&lt;p&gt;User data is a script that runs &lt;strong&gt;once, on first boot&lt;/strong&gt;, as root. This is how you bootstrap a fresh instance — install packages, pull config, start your app — without manually SSHing in. Essential for automation and Auto Scaling.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="c"&gt;# user-data.sh — bootstrap a simple web server on first boot&lt;/span&gt;
yum update &lt;span class="nt"&gt;-y&lt;/span&gt;
yum &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; nginx
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;h1&amp;gt;Deployed via user data&amp;lt;/h1&amp;gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /usr/share/nginx/html/index.html
systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;nginx
systemctl start nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Launching an Instance via CLI
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Launch a t3.micro with the bootstrap script and our key&lt;/span&gt;
aws ec2 run-instances &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--image-id&lt;/span&gt; ami-0abcdef1234567890 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--instance-type&lt;/span&gt; t3.micro &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--key-name&lt;/span&gt; my-devops-key &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--security-group-ids&lt;/span&gt; sg-0123456789abcdef0 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--subnet-id&lt;/span&gt; subnet-0123456789abcdef0 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--user-data&lt;/span&gt; file://user-data.sh &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--tag-specifications&lt;/span&gt; &lt;span class="s1"&gt;'ResourceType=instance,Tags=[{Key=Name,Value=web-01}]'&lt;/span&gt;

&lt;span class="c"&gt;# See what's running (handy filtered view)&lt;/span&gt;
aws ec2 describe-instances &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--filters&lt;/span&gt; &lt;span class="s2"&gt;"Name=instance-state-name,Values=running"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s1"&gt;'Reservations[].Instances[].{ID:InstanceId,Type:InstanceType,IP:PublicIpAddress}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; table
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Pricing Models
&lt;/h4&gt;

&lt;p&gt;Same instance, very different bill depending on how you buy it. Interviewers love this.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;On-Demand&lt;/strong&gt; — pay per second, no commitment. Most expensive per hour, max flexibility. Use for unpredictable/short workloads and testing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reserved Instances (RI)&lt;/strong&gt; — commit to a specific instance type for 1 or 3 years. Up to ~72% off. Use for steady, predictable baseline load.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Savings Plans&lt;/strong&gt; — commit to a $/hour spend for 1–3 years; more flexible than RIs (applies across families/regions). The modern preferred way to save on steady load.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Spot Instances&lt;/strong&gt; — bid on spare AWS capacity, up to ~90% off, but AWS can reclaim them with a 2-minute warning. Use for fault-tolerant, stateless, interruptible work (CI builds, batch jobs, big data).&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; A typical production setup mixes them: &lt;strong&gt;Reserved/Savings Plans&lt;/strong&gt; for the always-on baseline (web servers running 24/7), &lt;strong&gt;Spot&lt;/strong&gt; for the Auto Scaling group that handles traffic spikes and nightly batch jobs, and &lt;strong&gt;On-Demand&lt;/strong&gt; as fallback when Spot capacity isn't available. This can cut a compute bill by half or more.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Lambda vs EC2 — Serverless or Server?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Lambda&lt;/strong&gt; runs your code in response to events (an HTTP request via API Gateway, a file landing in S3, a message in a queue) with zero servers to manage. AWS provisions, scales, and bills only for execution time (per millisecond). You give it a function; it runs it. But it's short-lived (15-min max), stateless, and you don't control the OS.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;EC2&lt;/th&gt;
&lt;th&gt;Lambda&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Model&lt;/td&gt;
&lt;td&gt;Long-running virtual server&lt;/td&gt;
&lt;td&gt;Event-driven function (serverless)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;You manage&lt;/td&gt;
&lt;td&gt;OS, patching, scaling, capacity&lt;/td&gt;
&lt;td&gt;Just your code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scaling&lt;/td&gt;
&lt;td&gt;Manual / Auto Scaling groups&lt;/td&gt;
&lt;td&gt;Automatic, instant, per-request&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Billing&lt;/td&gt;
&lt;td&gt;Per second while running (even idle)&lt;/td&gt;
&lt;td&gt;Per ms of execution; $0 when idle&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Max runtime&lt;/td&gt;
&lt;td&gt;Runs forever&lt;/td&gt;
&lt;td&gt;15 minutes per invocation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;State&lt;/td&gt;
&lt;td&gt;Stateful (local disk, memory persist)&lt;/td&gt;
&lt;td&gt;Stateless (cold starts possible)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best for&lt;/td&gt;
&lt;td&gt;Steady traffic, full control, legacy apps, databases&lt;/td&gt;
&lt;td&gt;Spiky/event-driven work, APIs, glue code, cron jobs&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; "When would you NOT use Lambda?" Strong answer: long-running jobs (&amp;gt;15 min), workloads needing predictable low latency (cold starts hurt), apps requiring specific OS/runtime control, or steady high-volume traffic where always-on EC2 is actually cheaper. Lambda shines for bursty, event-driven, "scale-to-zero" workloads.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Containers: ECS vs EKS vs Fargate
&lt;/h3&gt;

&lt;p&gt;First, separate two things: the &lt;strong&gt;orchestrator&lt;/strong&gt; (decides where containers run, restarts them, scales them) from the &lt;strong&gt;compute&lt;/strong&gt; (the actual machines they run on).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ECS&lt;/strong&gt; (Elastic Container Service) — AWS's own orchestrator. Simpler, deeply AWS-integrated, no K8s knowledge needed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;EKS&lt;/strong&gt; (Elastic Kubernetes Service) — managed Kubernetes. Industry-standard, portable across clouds, bigger learning curve.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fargate&lt;/strong&gt; — NOT an orchestrator; it's a &lt;em&gt;serverless compute engine&lt;/em&gt;. It runs containers for ECS or EKS without you managing any EC2 nodes. "Containers without servers."&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;ECS&lt;/th&gt;
&lt;th&gt;EKS&lt;/th&gt;
&lt;th&gt;Fargate&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;What it is&lt;/td&gt;
&lt;td&gt;AWS-native orchestrator&lt;/td&gt;
&lt;td&gt;Managed Kubernetes orchestrator&lt;/td&gt;
&lt;td&gt;Serverless compute for containers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Runs on&lt;/td&gt;
&lt;td&gt;EC2 or Fargate&lt;/td&gt;
&lt;td&gt;EC2 or Fargate&lt;/td&gt;
&lt;td&gt;N/A (it IS the compute)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Learning curve&lt;/td&gt;
&lt;td&gt;Low (AWS-specific)&lt;/td&gt;
&lt;td&gt;High (full K8s)&lt;/td&gt;
&lt;td&gt;Low — no servers to manage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Portability&lt;/td&gt;
&lt;td&gt;AWS-locked&lt;/td&gt;
&lt;td&gt;Portable (standard K8s)&lt;/td&gt;
&lt;td&gt;AWS-only billing model&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;You manage nodes?&lt;/td&gt;
&lt;td&gt;Yes (if EC2 mode)&lt;/td&gt;
&lt;td&gt;Yes (if EC2 mode)&lt;/td&gt;
&lt;td&gt;No — AWS handles it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best for&lt;/td&gt;
&lt;td&gt;Simple AWS-only container apps&lt;/td&gt;
&lt;td&gt;K8s-standard, multi-cloud, complex orchestration&lt;/td&gt;
&lt;td&gt;Anyone who wants containers without managing servers&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; "ECS vs EKS" and "Fargate" are not three competitors — Fargate is a &lt;em&gt;launch type&lt;/em&gt; for ECS or EKS. A common combo is "ECS on Fargate": AWS-native simplicity + zero server management. Pick EKS only when you genuinely need Kubernetes (portability, team already knows K8s, complex workloads).&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Networking — VPC and Friends
&lt;/h2&gt;

&lt;p&gt;Networking is where a lot of DevOps engineers get stuck — and where interviewers love to probe. The good news: you already know networking basics (IPs, CIDR, routing). AWS just gives you software-defined versions of the same physical concepts. For a deeper treatment of subnets, CIDR design, and routing, see &lt;a href="https://opscanopy.com/learn/guides/networking-for-devops/" rel="noopener noreferrer"&gt;Networking for DevOps&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;/p&gt;
A VPC with a public subnet (IGW + NAT Gateway) and a private subnet (EC2); public traffic enters via the IGW while EC2 reaches the internet outbound through the NAT Gateway.





&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; A &lt;strong&gt;VPC&lt;/strong&gt; is your own private gated society (housing colony) inside AWS's huge city. &lt;strong&gt;Subnets&lt;/strong&gt; are blocks within the society. The &lt;strong&gt;route table&lt;/strong&gt; is the society's road map (which road leads where). The &lt;strong&gt;Internet Gateway&lt;/strong&gt; is the main gate to the outside world. &lt;strong&gt;Security Groups&lt;/strong&gt; are guards at each house's door; &lt;strong&gt;NACLs&lt;/strong&gt; are guards at the block's entrance.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  VPC (Virtual Private Cloud)
&lt;/h3&gt;

&lt;p&gt;A VPC is your own isolated virtual network inside AWS. Nothing outside it can reach in unless you explicitly allow it. You define its IP range using a &lt;strong&gt;CIDR block&lt;/strong&gt;, e.g. &lt;code&gt;10.0.0.0/16&lt;/code&gt; — that gives you ~65,536 private IP addresses to carve up. Size your VPC subnets with the &lt;a href="https://opscanopy.com/subnet-calculator/" rel="noopener noreferrer"&gt;Subnet Calculator&lt;/a&gt; and validate CIDR ranges with the &lt;a href="https://opscanopy.com/cidr-checker/" rel="noopener noreferrer"&gt;CIDR Checker&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  CIDR Blocks — Quick Refresher
&lt;/h3&gt;

&lt;p&gt;CIDR notation = IP + "how many bits are fixed." &lt;code&gt;/16&lt;/code&gt; fixes the first 16 bits → &lt;code&gt;10.0.x.x&lt;/code&gt; is yours (65,536 IPs). &lt;code&gt;/24&lt;/code&gt; fixes 24 bits → &lt;code&gt;10.0.1.x&lt;/code&gt; (256 IPs). Smaller number after the slash = bigger network. You give the VPC a large block (&lt;code&gt;/16&lt;/code&gt;) and slice subnets out of it (&lt;code&gt;/24&lt;/code&gt;).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; AWS reserves &lt;strong&gt;5 IPs in every subnet&lt;/strong&gt; (network address, VPC router, DNS, future use, broadcast). So a &lt;code&gt;/24&lt;/code&gt; subnet gives you 256 − 5 = 251 usable IPs, not 256. This trips people up when sizing subnets.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Subnets: Public vs Private
&lt;/h3&gt;

&lt;p&gt;A subnet is a slice of the VPC, and it lives in &lt;strong&gt;one Availability Zone&lt;/strong&gt;. The big question: &lt;em&gt;what actually makes a subnet "public"?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;It's NOT a checkbox called "public." A subnet is &lt;strong&gt;public&lt;/strong&gt; if and only if its &lt;strong&gt;route table has a route to an Internet Gateway&lt;/strong&gt; (&lt;code&gt;0.0.0.0/0 → igw-xxxx&lt;/code&gt;). A &lt;strong&gt;private&lt;/strong&gt; subnet has no such route — its traffic can't reach the internet directly.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Classic question: "What makes a subnet public?" Wrong answer: "it has public IPs." Correct answer: &lt;strong&gt;its route table routes &lt;code&gt;0.0.0.0/0&lt;/code&gt; to an Internet Gateway.&lt;/strong&gt; An instance also needs a public IP to be reachable, but the defining factor is the route to the IGW.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Route Tables
&lt;/h3&gt;

&lt;p&gt;A route table is a set of rules deciding where network traffic goes. Each subnet is associated with exactly one route table. Every table has a &lt;strong&gt;local&lt;/strong&gt; route (so subnets within the VPC can talk to each other automatically). You add routes for outside traffic — to an IGW (public) or a NAT Gateway (private).&lt;/p&gt;

&lt;h3&gt;
  
  
  Internet Gateway (IGW)
&lt;/h3&gt;

&lt;p&gt;The IGW is a horizontally-scaled, highly-available component attached to your VPC that allows &lt;strong&gt;two-way&lt;/strong&gt; communication between the VPC and the internet. One IGW per VPC. Without it, nothing in your VPC can reach the public internet, period.&lt;/p&gt;

&lt;h3&gt;
  
  
  NAT Gateway — Why Private Subnets Need It
&lt;/h3&gt;

&lt;p&gt;Your app servers sit in a &lt;strong&gt;private&lt;/strong&gt; subnet (no IGW route — good, attackers can't reach them). But they still need to download OS updates, pull packages, call external APIs. How do they reach &lt;em&gt;out&lt;/em&gt; without being reachable &lt;em&gt;in&lt;/em&gt;?&lt;/p&gt;

&lt;p&gt;Answer: a &lt;strong&gt;NAT Gateway&lt;/strong&gt;. It lives in a &lt;em&gt;public&lt;/em&gt; subnet and does Network Address Translation. Private instances route &lt;code&gt;0.0.0.0/0 → NAT Gateway&lt;/code&gt;; the NAT forwards their outbound requests to the internet using its own public IP, and returns the responses. Crucially, NAT only allows &lt;strong&gt;outbound-initiated&lt;/strong&gt; traffic — the outside world cannot start a connection back in.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; NAT Gateway is like a hotel front desk. Guests (private servers) can call out for room service or order food from outside, but no outsider can directly walk to your room — they only reach the front desk. Calls go out, randos can't come in.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; NAT Gateways are NOT free — you pay per hour &lt;em&gt;and&lt;/em&gt; per GB processed. They're a common surprise on AWS bills. Also, a NAT Gateway lives in one AZ; for high availability you deploy one per AZ. Don't put it in a private subnet by mistake (it needs the IGW route to work).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Request Flow: User to App
&lt;/h3&gt;

&lt;p&gt;Walking through a request to a web app makes it click:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;User's browser sends a request to your app's public IP / load balancer.&lt;/li&gt;
&lt;li&gt;Traffic hits the &lt;strong&gt;Internet Gateway&lt;/strong&gt; (the VPC's front gate).&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;public subnet's route table&lt;/strong&gt; directs it to the resource (e.g. a load balancer or web server with a public IP).&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;NACL&lt;/strong&gt; (block-level guard) checks subnet rules, then the &lt;strong&gt;Security Group&lt;/strong&gt; (door-level guard) checks if the port is allowed.&lt;/li&gt;
&lt;li&gt;The web tier forwards to app servers in the &lt;strong&gt;private subnet&lt;/strong&gt; over the VPC's internal &lt;code&gt;local&lt;/code&gt; route.&lt;/li&gt;
&lt;li&gt;Response travels back out the same path. Because SGs are stateful, the return traffic is automatically allowed.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Security Groups vs NACLs
&lt;/h3&gt;

&lt;p&gt;This is one of the most asked AWS interview questions. Both are virtual firewalls, but they operate at different levels and behave differently.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Security Group (SG)&lt;/strong&gt; — attached to the &lt;strong&gt;instance/ENI&lt;/strong&gt; (the door). &lt;strong&gt;Stateful&lt;/strong&gt;: if you allow inbound traffic, the response is automatically allowed out (and vice versa). &lt;strong&gt;Allow rules only&lt;/strong&gt; — you can't write a "deny" rule; anything not allowed is implicitly denied.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NACL (Network ACL)&lt;/strong&gt; — attached to the &lt;strong&gt;subnet&lt;/strong&gt; (the block gate). &lt;strong&gt;Stateless&lt;/strong&gt;: you must explicitly allow both inbound AND the return outbound traffic. Supports &lt;strong&gt;allow AND deny&lt;/strong&gt; rules, evaluated in numbered order.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Security Group&lt;/th&gt;
&lt;th&gt;NACL&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Level&lt;/td&gt;
&lt;td&gt;Instance / ENI&lt;/td&gt;
&lt;td&gt;Subnet&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;State&lt;/td&gt;
&lt;td&gt;Stateful (return traffic auto-allowed)&lt;/td&gt;
&lt;td&gt;Stateless (must allow both directions)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rules&lt;/td&gt;
&lt;td&gt;Allow only&lt;/td&gt;
&lt;td&gt;Allow AND Deny&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rule evaluation&lt;/td&gt;
&lt;td&gt;All rules evaluated together&lt;/td&gt;
&lt;td&gt;In numbered order, first match wins&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Default behavior&lt;/td&gt;
&lt;td&gt;Deny all inbound, allow all outbound&lt;/td&gt;
&lt;td&gt;Default NACL allows all; custom denies all&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Use case&lt;/td&gt;
&lt;td&gt;Primary, everyday instance firewall&lt;/td&gt;
&lt;td&gt;Coarse subnet-wide rules, blocking specific IPs&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Memorize the one-liner: &lt;strong&gt;"SG = stateful, instance-level, allow-only. NACL = stateless, subnet-level, allow + deny."&lt;/strong&gt; Follow-up: "Can you block a single malicious IP?" → Yes, with a NACL &lt;em&gt;deny&lt;/em&gt; rule (SGs can't deny). In practice, 95% of your work is in Security Groups; NACLs are a secondary, coarse layer.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Building VPC with the CLI
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Create the VPC (grab the returned VpcId)&lt;/span&gt;
aws ec2 create-vpc &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--cidr-block&lt;/span&gt; 10.0.0.0/16 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--tag-specifications&lt;/span&gt; &lt;span class="s1"&gt;'ResourceType=vpc,Tags=[{Key=Name,Value=my-vpc}]'&lt;/span&gt;

&lt;span class="c"&gt;# 2. Create a public subnet inside it&lt;/span&gt;
aws ec2 create-subnet &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--vpc-id&lt;/span&gt; vpc-0123456789abcdef0 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--cidr-block&lt;/span&gt; 10.0.1.0/24 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--availability-zone&lt;/span&gt; ap-south-1a &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--tag-specifications&lt;/span&gt; &lt;span class="s1"&gt;'ResourceType=subnet,Tags=[{Key=Name,Value=public-1a}]'&lt;/span&gt;

&lt;span class="c"&gt;# 3. Create a security group in the VPC&lt;/span&gt;
aws ec2 create-security-group &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--group-name&lt;/span&gt; web-sg &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--description&lt;/span&gt; &lt;span class="s2"&gt;"Allow HTTP and SSH"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--vpc-id&lt;/span&gt; vpc-0123456789abcdef0

&lt;span class="c"&gt;# 4. Allow inbound HTTP (80) from anywhere and SSH (22) from your IP&lt;/span&gt;
aws ec2 authorize-security-group-ingress &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--group-id&lt;/span&gt; sg-0123456789abcdef0 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--protocol&lt;/span&gt; tcp &lt;span class="nt"&gt;--port&lt;/span&gt; 80 &lt;span class="nt"&gt;--cidr&lt;/span&gt; 0.0.0.0/0

aws ec2 authorize-security-group-ingress &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--group-id&lt;/span&gt; sg-0123456789abcdef0 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--protocol&lt;/span&gt; tcp &lt;span class="nt"&gt;--port&lt;/span&gt; 22 &lt;span class="nt"&gt;--cidr&lt;/span&gt; 203.0.113.25/32
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; Never open SSH (port 22) to &lt;code&gt;0.0.0.0/0&lt;/code&gt; — that exposes your server to the entire internet's brute-force bots. Always restrict to your own IP (&lt;code&gt;/32&lt;/code&gt;) or use a bastion host / AWS Systems Manager Session Manager (no open SSH port at all). This is a top finding in every security audit.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Storage — S3, EBS, EFS
&lt;/h2&gt;

&lt;p&gt;AWS storage comes in three fundamental flavors, and knowing which to reach for is core DevOps knowledge: &lt;strong&gt;object&lt;/strong&gt; storage (S3), &lt;strong&gt;block&lt;/strong&gt; storage (EBS), and &lt;strong&gt;file&lt;/strong&gt; storage (EFS).&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;br&gt;
  &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  &lt;br&gt;
  &lt;/p&gt;
S3 is object storage accessed over HTTPS; EBS is a block volume attached to a single EC2 instance; EFS is a shared NFS file system mountable by many instances simultaneously.





&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;strong&gt;EBS (block)&lt;/strong&gt; = a hard drive plugged into one computer — fast, personal, one machine at a time. &lt;strong&gt;EFS (file)&lt;/strong&gt; = a shared network drive in the office that everyone mounts at once. &lt;strong&gt;S3 (object)&lt;/strong&gt; = a giant warehouse where you drop labelled boxes and fetch them by name via a web address — infinite shelves, but you can't "edit" a box, you replace it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  S3 — Simple Storage Service
&lt;/h3&gt;

&lt;p&gt;S3 is object storage: you store &lt;strong&gt;objects&lt;/strong&gt; (files + metadata) inside &lt;strong&gt;buckets&lt;/strong&gt; (globally-unique named containers). It's accessed over HTTPS via an API/URL, scales infinitely, and is 99.999999999% (11 nines) durable. You don't mount it like a disk — you GET/PUT objects by key.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; Bucket names are &lt;strong&gt;globally unique across all AWS accounts on Earth&lt;/strong&gt; — like domain names. If someone took &lt;code&gt;my-bucket&lt;/code&gt;, you can't. Use a prefix like &lt;code&gt;mycompany-prod-logs-2026&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Versioning
&lt;/h4&gt;

&lt;p&gt;Enable versioning and S3 keeps every version of an object. Overwrite or delete? The old version is retained (a "delete marker" is added instead of true deletion). This protects against accidental overwrites and ransomware. Once enabled, it can only be suspended, not fully disabled.&lt;/p&gt;

&lt;h4&gt;
  
  
  Lifecycle Policies
&lt;/h4&gt;

&lt;p&gt;Rules that automatically move or expire objects over time to save money. Example: keep logs in Standard for 30 days, move to Infrequent Access, then to Glacier after 90 days, and delete after a year — all automatic. This is how you control storage costs at scale.&lt;/p&gt;

&lt;h4&gt;
  
  
  Storage Classes
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;S3 Standard&lt;/strong&gt; — frequent access, lowest latency. Default for active data, website assets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;S3 Standard-IA (Infrequent Access)&lt;/strong&gt; — cheaper storage, but you pay a retrieval fee. For data accessed rarely but needed instantly (backups, older logs).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;S3 Glacier (Flexible / Deep Archive)&lt;/strong&gt; — dirt cheap archival; retrieval takes minutes to hours. For compliance archives, long-term backups you rarely touch.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;S3 Intelligent-Tiering&lt;/strong&gt; — AWS auto-moves objects between tiers based on access patterns. Set-and-forget cost optimization.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Don't manually guess tiers for unpredictable access — use &lt;strong&gt;Intelligent-Tiering&lt;/strong&gt; and let AWS optimize. Use explicit &lt;strong&gt;lifecycle policies&lt;/strong&gt; when you KNOW the pattern (e.g., "logs are useless after 90 days").&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Static Website Hosting
&lt;/h4&gt;

&lt;p&gt;S3 can serve a static website (HTML/CSS/JS) directly — perfect for your React build output. Upload the build, enable website hosting, set the index document. Pair it with CloudFront (CDN) + an ACM certificate for HTTPS and global caching.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create a bucket&lt;/span&gt;
aws s3 mb s3://mycompany-react-app-2026 &lt;span class="nt"&gt;--region&lt;/span&gt; ap-south-1

&lt;span class="c"&gt;# Upload your React production build&lt;/span&gt;
aws s3 &lt;span class="nb"&gt;sync&lt;/span&gt; ./build s3://mycompany-react-app-2026 &lt;span class="nt"&gt;--delete&lt;/span&gt;

&lt;span class="c"&gt;# Enable static website hosting&lt;/span&gt;
aws s3 website s3://mycompany-react-app-2026 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--index-document&lt;/span&gt; index.html &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--error-document&lt;/span&gt; index.html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Security: Block Public Access and Bucket Policies
&lt;/h4&gt;

&lt;p&gt;Leaky S3 buckets are the most famous cause of data breaches. AWS now enables &lt;strong&gt;Block Public Access&lt;/strong&gt; by default — a master switch that overrides any policy trying to make a bucket public. Access is otherwise controlled by &lt;strong&gt;bucket policies&lt;/strong&gt; (JSON rules on the bucket) and IAM policies (rules on users/roles).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Lock down public access (the safe default)&lt;/span&gt;
aws s3api put-public-access-block &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--bucket&lt;/span&gt; mycompany-react-app-2026 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--public-access-block-configuration&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nv"&gt;BlockPublicAcls&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;,IgnorePublicAcls&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;,BlockPublicPolicy&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;,RestrictPublicBuckets&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;

&lt;span class="c"&gt;# Enable versioning for safety&lt;/span&gt;
aws s3api put-bucket-versioning &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--bucket&lt;/span&gt; mycompany-react-app-2026 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--versioning-configuration&lt;/span&gt; &lt;span class="nv"&gt;Status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Enabled
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; For a public website, the modern best practice is NOT to make the S3 bucket public. Instead keep Block Public Access ON and serve through &lt;strong&gt;CloudFront with an Origin Access Control (OAC)&lt;/strong&gt;, so only CloudFront can read the bucket. Public buckets should be a deliberate, rare exception.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  EBS — Elastic Block Store
&lt;/h3&gt;

&lt;p&gt;EBS is a &lt;strong&gt;block storage&lt;/strong&gt; volume — a virtual hard disk you attach to a single EC2 instance. You format it, mount it, and it behaves like a local SSD. It's the root disk of most instances and where databases store data. It lives in &lt;strong&gt;one AZ&lt;/strong&gt; and (traditionally) attaches to &lt;strong&gt;one instance at a time&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create a 50 GB gp3 SSD volume in a specific AZ&lt;/span&gt;
aws ec2 create-volume &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume-type&lt;/span&gt; gp3 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--size&lt;/span&gt; 50 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--availability-zone&lt;/span&gt; ap-south-1a &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--tag-specifications&lt;/span&gt; &lt;span class="s1"&gt;'ResourceType=volume,Tags=[{Key=Name,Value=db-data}]'&lt;/span&gt;

&lt;span class="c"&gt;# Attach it to an instance (volume must be in the same AZ as the instance)&lt;/span&gt;
aws ec2 attach-volume &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume-id&lt;/span&gt; vol-0123456789abcdef0 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--instance-id&lt;/span&gt; i-0123456789abcdef0 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--device&lt;/span&gt; /dev/sdf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; EBS and EC2 must be in the &lt;strong&gt;same Availability Zone&lt;/strong&gt; to attach — block storage is AZ-bound. To move data across AZs, take a &lt;strong&gt;snapshot&lt;/strong&gt; (stored in S3 behind the scenes) and create a new volume from it in the target AZ.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  EFS — Elastic File System
&lt;/h3&gt;

&lt;p&gt;EFS is &lt;strong&gt;file storage&lt;/strong&gt; — a managed NFS share that &lt;strong&gt;many EC2 instances mount simultaneously&lt;/strong&gt;, across multiple AZs. It grows and shrinks automatically. Use it when a fleet of servers needs to read/write the same files (shared uploads, CMS content, web cluster assets).&lt;/p&gt;

&lt;h3&gt;
  
  
  Block vs File vs Object — When to Use Which
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;S3 (Object)&lt;/th&gt;
&lt;th&gt;EBS (Block)&lt;/th&gt;
&lt;th&gt;EFS (File)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Type&lt;/td&gt;
&lt;td&gt;Object store&lt;/td&gt;
&lt;td&gt;Block volume (virtual disk)&lt;/td&gt;
&lt;td&gt;Shared file system (NFS)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Access&lt;/td&gt;
&lt;td&gt;HTTPS API / URL&lt;/td&gt;
&lt;td&gt;Mounted to one EC2&lt;/td&gt;
&lt;td&gt;Mounted to many EC2s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Attaches to&lt;/td&gt;
&lt;td&gt;Anything over the internet&lt;/td&gt;
&lt;td&gt;1 instance (1 AZ)&lt;/td&gt;
&lt;td&gt;Many instances, multi-AZ&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scaling&lt;/td&gt;
&lt;td&gt;Infinite, automatic&lt;/td&gt;
&lt;td&gt;Fixed size (resize manually)&lt;/td&gt;
&lt;td&gt;Automatic, elastic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Can edit in place?&lt;/td&gt;
&lt;td&gt;No — replace whole object&lt;/td&gt;
&lt;td&gt;Yes — random read/write&lt;/td&gt;
&lt;td&gt;Yes — random read/write&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best for&lt;/td&gt;
&lt;td&gt;Assets, backups, logs, static sites, big data&lt;/td&gt;
&lt;td&gt;OS root disk, databases, single-server data&lt;/td&gt;
&lt;td&gt;Shared storage across a server fleet&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; The killer interview line: &lt;strong&gt;"EBS = one disk for one server; EFS = one shared drive for many servers; S3 = object storage accessed over HTTP, not mounted."&lt;/strong&gt; If asked "where do you store database files?" → EBS (low latency block). "Shared files across an Auto Scaling group?" → EFS. "Static assets / backups?" → S3.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Databases — RDS and DynamoDB
&lt;/h2&gt;

&lt;p&gt;As a DevOps engineer, the database is the part nobody wants to babysit at 3am — so AWS gives you &lt;strong&gt;managed&lt;/strong&gt; database services. You stop being a DBA and start being an operator who scripts the boring stuff away.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Running a DB on a raw EC2 instance is like buying a car and also building your own service garage — you patch the engine, change the oil, handle backups. A &lt;em&gt;managed&lt;/em&gt; database (RDS/DynamoDB) is like a chauffeur-driven car with a maintenance contract: you write business logic, AWS handles patching, backups, and failover.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Amazon RDS — Managed Relational
&lt;/h3&gt;

&lt;p&gt;RDS = your familiar SQL world (tables, joins, foreign keys, transactions) but AWS handles provisioning, patching, backups, and failover. You pick an &lt;strong&gt;engine&lt;/strong&gt; and AWS runs it for you.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Engines:&lt;/strong&gt; PostgreSQL, MySQL, MariaDB, Oracle, SQL Server, and Amazon Aurora (AWS's high-performance MySQL/Postgres-compatible engine).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-AZ (High Availability):&lt;/strong&gt; AWS keeps a synchronous standby replica in a second Availability Zone. If the primary dies, RDS automatically fails over to the standby (DNS endpoint stays the same). This is about &lt;em&gt;survival&lt;/em&gt;, not speed — the standby does not serve reads.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Read Replicas (Scale):&lt;/strong&gt; Asynchronous copies you can read from to offload heavy SELECT traffic (reporting, dashboards). This is about &lt;em&gt;scaling reads&lt;/em&gt;, not failover. You can promote a replica to standalone, but it isn't automatic HA.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automated backups:&lt;/strong&gt; Daily snapshot + transaction logs enable point-in-time recovery (PITR) within your retention window (up to 35 days). &lt;strong&gt;Manual snapshots&lt;/strong&gt; live until you delete them — good for "before the migration" safety copies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parameter groups:&lt;/strong&gt; tune engine settings (e.g. &lt;code&gt;max_connections&lt;/code&gt;) without touching a config file. &lt;strong&gt;Subnet groups:&lt;/strong&gt; tell RDS which subnets (across AZs) it may launch into — almost always your &lt;em&gt;private&lt;/em&gt; subnets.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; Multi-AZ and Read Replica are constantly confused in interviews. &lt;strong&gt;Multi-AZ = HA/failover (sync, same endpoint, no read offload). Read Replica = scaling reads (async, separate endpoint).&lt;/strong&gt; You can have both at once.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; An e-commerce app runs RDS Postgres Multi-AZ for the orders database. The analytics team was hammering it with giant reports, slowing checkout. The fix: add two Read Replicas and point the reporting tool at the replica endpoint. Checkout latency dropped instantly — writes stayed on the primary, reads spread out.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Create a Multi-AZ Postgres instance in private subnets:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws rds create-db-instance &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--db-instance-identifier&lt;/span&gt; prod-orders-db &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--db-instance-class&lt;/span&gt; db.t3.medium &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--engine&lt;/span&gt; postgres &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--engine-version&lt;/span&gt; 16.3 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--master-username&lt;/span&gt; appadmin &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--master-user-password&lt;/span&gt; &lt;span class="s1"&gt;'ChangeMe-UseSecretsManager!'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--allocated-storage&lt;/span&gt; 50 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--storage-type&lt;/span&gt; gp3 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--multi-az&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--db-subnet-group-name&lt;/span&gt; prod-private-subnets &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--vpc-security-group-ids&lt;/span&gt; sg-0a1b2c3d4e5f67890 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--backup-retention-period&lt;/span&gt; 7 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--storage-encrypted&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--no-publicly-accessible&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Never bake the DB password into CLI/Terraform. Use &lt;strong&gt;AWS Secrets Manager&lt;/strong&gt; (RDS can even auto-rotate it) or &lt;code&gt;--manage-master-user-password&lt;/code&gt; so AWS stores and rotates it for you.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Why Managed Beats Self-Hosted on EC2
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Automated patching, backups, and PITR — no cron jobs you forget about.&lt;/li&gt;
&lt;li&gt;One-flag HA (&lt;code&gt;--multi-az&lt;/code&gt;) instead of hand-building replication and failover scripts.&lt;/li&gt;
&lt;li&gt;Push-button read scaling and storage autoscaling.&lt;/li&gt;
&lt;li&gt;You still own schema, queries, and indexes — i.e. the stuff that's actually your job.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Amazon DynamoDB — Managed NoSQL
&lt;/h3&gt;

&lt;p&gt;DynamoDB is a fully managed key-value / document store. No servers, no engine versions, no connection pools — just an API. It delivers &lt;strong&gt;single-digit millisecond latency at any scale&lt;/strong&gt;, which is why it powers shopping carts, session stores, and leaderboards.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Partition key (PK):&lt;/strong&gt; determines which physical partition stores the item — choose a high-cardinality key so traffic spreads evenly (avoid "hot partitions").&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sort key (SK):&lt;/strong&gt; optional; lets you store many related items under one PK and query ranges (e.g. PK=&lt;code&gt;USER#42&lt;/code&gt;, SK=&lt;code&gt;ORDER#2026-06-01&lt;/code&gt;). PK + SK together form the primary key.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;On-demand capacity:&lt;/strong&gt; pay per request, scales instantly — perfect for spiky or unknown traffic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Provisioned capacity:&lt;/strong&gt; you set read/write capacity units (with optional auto-scaling) — cheaper for steady, predictable load.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws dynamodb create-table &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--table-name&lt;/span&gt; UserOrders &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--attribute-definitions&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nv"&gt;AttributeName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;UserId,AttributeType&lt;span class="o"&gt;=&lt;/span&gt;S &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nv"&gt;AttributeName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;OrderId,AttributeType&lt;span class="o"&gt;=&lt;/span&gt;S &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--key-schema&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nv"&gt;AttributeName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;UserId,KeyType&lt;span class="o"&gt;=&lt;/span&gt;HASH &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nv"&gt;AttributeName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;OrderId,KeyType&lt;span class="o"&gt;=&lt;/span&gt;RANGE &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--billing-mode&lt;/span&gt; PAY_PER_REQUEST &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--tags&lt;/span&gt; &lt;span class="nv"&gt;Key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;env&lt;/span&gt;,Value&lt;span class="o"&gt;=&lt;/span&gt;prod
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; &lt;code&gt;HASH&lt;/code&gt; = partition key, &lt;code&gt;RANGE&lt;/code&gt; = sort key in DynamoDB CLI terminology. &lt;code&gt;PAY_PER_REQUEST&lt;/code&gt; = on-demand; switch to &lt;code&gt;PROVISIONED&lt;/code&gt; with &lt;code&gt;--provisioned-throughput&lt;/code&gt; once your traffic is predictable.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  RDS vs DynamoDB — When to Pick What
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Pick RDS&lt;/strong&gt; when you have relational data, complex joins, transactions, and ad-hoc queries (finance, orders, anything with strong consistency and relationships). &lt;strong&gt;Pick DynamoDB&lt;/strong&gt; when you need massive scale, predictable access patterns by key, ultra-low latency, and zero ops (carts, sessions, IoT, gaming, event logs).&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Amazon RDS&lt;/th&gt;
&lt;th&gt;DynamoDB&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Data model&lt;/td&gt;
&lt;td&gt;Relational (tables, joins)&lt;/td&gt;
&lt;td&gt;NoSQL key-value / document&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Query flexibility&lt;/td&gt;
&lt;td&gt;Full SQL, ad-hoc queries&lt;/td&gt;
&lt;td&gt;Key-based access; design schema around access patterns&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scaling&lt;/td&gt;
&lt;td&gt;Vertical + read replicas&lt;/td&gt;
&lt;td&gt;Horizontal, virtually unlimited, automatic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Latency&lt;/td&gt;
&lt;td&gt;Low (ms), depends on instance&lt;/td&gt;
&lt;td&gt;Single-digit ms at any scale&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HA&lt;/td&gt;
&lt;td&gt;Multi-AZ failover&lt;/td&gt;
&lt;td&gt;Built-in, multi-AZ by default&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ops overhead&lt;/td&gt;
&lt;td&gt;Low (you pick instance size, params)&lt;/td&gt;
&lt;td&gt;Near zero (serverless)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best for&lt;/td&gt;
&lt;td&gt;Orders, finance, complex relationships&lt;/td&gt;
&lt;td&gt;Carts, sessions, IoT, leaderboards&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; "How do you scale reads on RDS without hurting writes?" → &lt;strong&gt;Read Replicas&lt;/strong&gt;, point read-only traffic at the replica endpoint. "How do you make RDS highly available?" → &lt;strong&gt;Multi-AZ&lt;/strong&gt;. Saying the right one for the right reason instantly signals you understand the difference.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  How It All Fits Together
&lt;/h2&gt;

&lt;p&gt;You've met the building blocks one by one — IAM, VPC, subnets, EC2, S3, RDS, Security Groups, gateways, ALB. Now let's snap them together like Lego and deploy a typical web app. This is the mental model interviewers want to hear, and it's the diagram you'll draw on a whiteboard for the rest of your career.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Think of your AWS account as a gated township. &lt;strong&gt;IAM&lt;/strong&gt; = the security desk deciding who enters and what they can touch. &lt;strong&gt;VPC&lt;/strong&gt; = the township's boundary wall. &lt;strong&gt;Subnets&lt;/strong&gt; = individual lanes (some facing the main road = public, some tucked inside = private). &lt;strong&gt;IGW&lt;/strong&gt; = the main gate to the public road. &lt;strong&gt;NAT&lt;/strong&gt; = a one-way courier so inside residents can order online without strangers walking in. &lt;strong&gt;Security Groups&lt;/strong&gt; = the lock on each house's door.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  The 3-Tier Architecture (Web / App / Data)
&lt;/h3&gt;

&lt;p&gt;Almost every classic web app splits into three tiers, and AWS maps cleanly onto them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Web / presentation tier:&lt;/strong&gt; the public entry point. An &lt;strong&gt;Application Load Balancer (ALB)&lt;/strong&gt; sits in &lt;em&gt;public subnets&lt;/em&gt; and accepts user traffic on 80/443. Static assets (images, JS bundles, your React build) often live in &lt;strong&gt;S3&lt;/strong&gt; and/or behind a CDN.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;App / logic tier:&lt;/strong&gt; your &lt;strong&gt;EC2&lt;/strong&gt; instances (or containers) running the backend, in &lt;em&gt;private subnets&lt;/em&gt;. They never get a public IP — the ALB forwards traffic to them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data tier:&lt;/strong&gt; &lt;strong&gt;RDS&lt;/strong&gt; (and/or DynamoDB) in &lt;em&gt;private subnets&lt;/em&gt;, reachable only from the app tier. Nothing on the internet can touch the database directly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each tier lives in its own subnet layer, spread across at least &lt;strong&gt;two Availability Zones&lt;/strong&gt; for resilience, all inside one VPC.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Deployment, Step by Step
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;IAM first.&lt;/strong&gt; Create roles, not long-lived keys. An EC2 &lt;em&gt;instance role&lt;/em&gt; lets the app read from S3 and pull DB credentials from Secrets Manager — no keys on disk.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build the VPC.&lt;/strong&gt; Carve a CIDR (e.g. &lt;code&gt;10.0.0.0/16&lt;/code&gt;) and create public + private subnets across two AZs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Attach an Internet Gateway (IGW)&lt;/strong&gt; to the VPC and route public subnets' &lt;code&gt;0.0.0.0/0&lt;/code&gt; to it. Add a &lt;strong&gt;NAT Gateway&lt;/strong&gt; in a public subnet so private instances can reach the internet outbound (for updates/patches) without being reachable inbound.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Launch EC2 app servers&lt;/strong&gt; in the private subnets, behind an Auto Scaling Group ideally.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create the ALB&lt;/strong&gt; in the public subnets, with a target group pointing at the EC2 instances. Health checks decide which instances get traffic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Provision RDS&lt;/strong&gt; (Multi-AZ) in the private subnets via a DB subnet group.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wire Security Groups (the real glue):&lt;/strong&gt; ALB SG allows 443 from &lt;code&gt;0.0.0.0/0&lt;/code&gt;; EC2 SG allows the app port &lt;em&gt;only from the ALB's SG&lt;/em&gt;; RDS SG allows 5432/3306 &lt;em&gt;only from the EC2 SG&lt;/em&gt;. Referencing SGs by ID (not IP ranges) is the clean, least-privilege way.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Point your domain&lt;/strong&gt; (Route 53) at the ALB and attach a TLS cert (ACM) for HTTPS.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Request Flow, End to End
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;User's browser resolves your domain via &lt;strong&gt;Route 53&lt;/strong&gt; → gets the ALB's address.&lt;/li&gt;
&lt;li&gt;Request enters the VPC through the &lt;strong&gt;IGW&lt;/strong&gt; and hits the &lt;strong&gt;ALB&lt;/strong&gt; in a public subnet (TLS terminated by ACM cert).&lt;/li&gt;
&lt;li&gt;ALB picks a healthy &lt;strong&gt;EC2&lt;/strong&gt; instance in a private subnet and forwards the request — allowed because the EC2 Security Group trusts the ALB's SG.&lt;/li&gt;
&lt;li&gt;The app reads/writes &lt;strong&gt;RDS&lt;/strong&gt; (private), pulls static files or uploads to &lt;strong&gt;S3&lt;/strong&gt;, and uses its &lt;strong&gt;IAM instance role&lt;/strong&gt; for those calls.&lt;/li&gt;
&lt;li&gt;If the app needs to call an external API, traffic exits via the &lt;strong&gt;NAT Gateway&lt;/strong&gt; — outbound only.&lt;/li&gt;
&lt;li&gt;Response flows back through the ALB to the user. The database and app servers were never exposed to the internet.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; A SaaS dashboard runs exactly this shape: ALB in two public subnets, an Auto Scaling Group of EC2 in two private subnets, RDS Postgres Multi-AZ in the data tier, and the React build served from S3 + CloudFront. When a marketing email caused a traffic spike, the ASG added instances and the ALB spread load — zero manual intervention, no downtime.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Where CI/CD and IaC Fit
&lt;/h3&gt;

&lt;p&gt;You'd never click all of the above by hand in production. &lt;strong&gt;Infrastructure as Code&lt;/strong&gt; — Terraform or CloudFormation — defines the VPC, subnets, SGs, ALB, RDS, and IAM roles as version-controlled files. You &lt;code&gt;plan&lt;/code&gt; and &lt;code&gt;apply&lt;/code&gt; to create identical environments (dev/staging/prod) repeatably.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CI/CD&lt;/strong&gt; (GitHub Actions, GitLab CI, CodePipeline) handles the &lt;em&gt;application&lt;/em&gt;: on every git push it builds, tests, and deploys your code to the EC2/containers and pushes the React bundle to S3. IaC builds the house; CI/CD keeps moving the furniture in.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; When asked "walk me through deploying a web app on AWS," structure your answer as &lt;strong&gt;tiers (web/app/data) + the security boundaries between them&lt;/strong&gt;. Emphasize that databases and app servers live in &lt;em&gt;private&lt;/em&gt; subnets, the ALB is the only public entry, and Security Groups reference each other by ID. That one paragraph signals senior-level thinking.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Hands-on AWS CLI
&lt;/h2&gt;

&lt;p&gt;The console is great for learning, but DevOps lives in the terminal — scriptable, repeatable, automatable. The AWS CLI is your daily driver. Let's install it, configure credentials, and build a cheat sheet you'll actually reuse.&lt;/p&gt;

&lt;h3&gt;
  
  
  Install on Ubuntu 24.04
&lt;/h3&gt;

&lt;p&gt;Use the official v2 bundle (the &lt;code&gt;apt&lt;/code&gt; package is often outdated):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; unzip curl
curl &lt;span class="s2"&gt;"https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip"&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="s2"&gt;"awscliv2.zip"&lt;/span&gt;
unzip awscliv2.zip
&lt;span class="nb"&gt;sudo&lt;/span&gt; ./aws/install
aws &lt;span class="nt"&gt;--version&lt;/span&gt;   &lt;span class="c"&gt;# confirm: aws-cli/2.x.x ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Configure Credentials
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;aws configure&lt;/code&gt; walks you through four prompts: Access Key ID, Secret Access Key, default region, and output format (&lt;code&gt;json&lt;/code&gt;, &lt;code&gt;table&lt;/code&gt;, or &lt;code&gt;text&lt;/code&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws configure
&lt;span class="c"&gt;# AWS Access Key ID [None]: AKIAEXAMPLE...&lt;/span&gt;
&lt;span class="c"&gt;# AWS Secret Access Key [None]: wJalr...EXAMPLEKEY&lt;/span&gt;
&lt;span class="c"&gt;# Default region name [None]: ap-south-1&lt;/span&gt;
&lt;span class="c"&gt;# Default output format [None]: json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Named Profiles and Where Credentials Live
&lt;/h3&gt;

&lt;p&gt;One laptop, many accounts (dev, prod, client). &lt;strong&gt;Named profiles&lt;/strong&gt; keep them separate. Credentials and settings are stored in two plaintext files in your home directory:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;~/.aws/credentials&lt;/code&gt; — the secret keys (per profile).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;~/.aws/config&lt;/code&gt; — region, output format, and SSO/role settings (per profile).
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create a named profile&lt;/span&gt;
aws configure &lt;span class="nt"&gt;--profile&lt;/span&gt; prod

&lt;span class="c"&gt;# Use it per-command&lt;/span&gt;
aws s3 &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;--profile&lt;/span&gt; prod

&lt;span class="c"&gt;# Or set it for the whole shell session&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;AWS_PROFILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;prod
aws sts get-caller-identity

&lt;span class="c"&gt;# Inspect the files&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; ~/.aws/config
&lt;span class="nb"&gt;cat&lt;/span&gt; ~/.aws/credentials
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; &lt;code&gt;~/.aws/credentials&lt;/code&gt; stores your secret key in &lt;strong&gt;plaintext&lt;/strong&gt;. Never commit this file, never paste keys into Slack, and on shared/CI machines prefer IAM roles or AWS SSO over long-lived keys.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Cheat Sheet — Identity and STS
&lt;/h3&gt;

&lt;p&gt;Your "whoami" — always run this first to confirm which identity/account you're operating as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws sts get-caller-identity            &lt;span class="c"&gt;# who am I? which account?&lt;/span&gt;
aws configure list                     &lt;span class="c"&gt;# active profile, region, source&lt;/span&gt;
aws configure list-profiles            &lt;span class="c"&gt;# all configured profiles&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Cheat Sheet — S3
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws s3 &lt;span class="nb"&gt;ls&lt;/span&gt;                                      &lt;span class="c"&gt;# list buckets&lt;/span&gt;
aws s3 &lt;span class="nb"&gt;ls &lt;/span&gt;s3://my-app-assets/                  &lt;span class="c"&gt;# list objects&lt;/span&gt;
aws s3 &lt;span class="nb"&gt;cp&lt;/span&gt; ./build/app.js s3://my-app-assets/   &lt;span class="c"&gt;# upload one file&lt;/span&gt;
aws s3 &lt;span class="nb"&gt;sync&lt;/span&gt; ./build/ s3://my-app-assets/ &lt;span class="nt"&gt;--delete&lt;/span&gt;   &lt;span class="c"&gt;# mirror a dir (great for React builds)&lt;/span&gt;
aws s3 rb s3://old-bucket &lt;span class="nt"&gt;--force&lt;/span&gt;              &lt;span class="c"&gt;# remove bucket + contents&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Cheat Sheet — EC2
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws ec2 describe-instances &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s1"&gt;'Reservations[].Instances[].{ID:InstanceId,State:State.Name,Type:InstanceType,IP:PrivateIpAddress}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; table
aws ec2 describe-security-groups &lt;span class="nt"&gt;--group-ids&lt;/span&gt; sg-0a1b2c3d
aws ec2 stop-instances  &lt;span class="nt"&gt;--instance-ids&lt;/span&gt; i-0123456789abcdef0
aws ec2 start-instances &lt;span class="nt"&gt;--instance-ids&lt;/span&gt; i-0123456789abcdef0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Cheat Sheet — IAM
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws iam list-users
aws iam list-roles &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s1"&gt;'Roles[].RoleName'&lt;/span&gt; &lt;span class="nt"&gt;--output&lt;/span&gt; text
aws iam list-attached-role-policies &lt;span class="nt"&gt;--role-name&lt;/span&gt; app-ec2-role
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Cheat Sheet — CloudWatch Logs
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws logs describe-log-groups
aws logs &lt;span class="nb"&gt;tail&lt;/span&gt; /aws/lambda/my-func &lt;span class="nt"&gt;--follow&lt;/span&gt;         &lt;span class="c"&gt;# live tail, like tail -f&lt;/span&gt;
aws logs &lt;span class="nb"&gt;tail&lt;/span&gt; /ecs/web-app &lt;span class="nt"&gt;--since&lt;/span&gt; 1h &lt;span class="nt"&gt;--format&lt;/span&gt; short
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Two habits that save careers: (1) keep a separate profile per account and run a quick &lt;code&gt;aws sts get-caller-identity&lt;/code&gt; before any destructive command — confirm you're not in prod. (2) For mutating EC2 actions, add &lt;code&gt;--dry-run&lt;/code&gt;: it checks permissions and returns &lt;em&gt;"would have succeeded"&lt;/em&gt; without actually doing anything. Combine with &lt;code&gt;--output table&lt;/code&gt; and &lt;code&gt;--query&lt;/code&gt; (JMESPath) to keep output readable.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Cost and Security Guardrails
&lt;/h2&gt;

&lt;p&gt;Two ways to get fired fast in cloud: a surprise five-figure bill, or a leaked access key that lets attackers mine crypto on your account. Both are 100% preventable with guardrails you set up once.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cost Guardrails
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Billing alerts / AWS Budgets:&lt;/strong&gt; set a monthly budget and get emailed at 50/80/100% — your early-warning system.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost Explorer:&lt;/strong&gt; visualize spend by service, account, and tag to find what's actually eating money.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tagging strategy:&lt;/strong&gt; tag every resource with &lt;code&gt;env&lt;/code&gt;, &lt;code&gt;team&lt;/code&gt;, &lt;code&gt;project&lt;/code&gt;, &lt;code&gt;owner&lt;/code&gt;. Without tags you can't attribute cost or clean up safely.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Right-sizing:&lt;/strong&gt; most instances run at &amp;lt;20% CPU. Use Compute Optimizer to downsize over-provisioned EC2/RDS.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Spot and Savings Plans:&lt;/strong&gt; Spot Instances (up to ~90% off) for fault-tolerant/batch workloads; Savings Plans / Reserved Instances for steady baseline compute.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kill idle resources&lt;/strong&gt; — the silent money drains:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Unattached Elastic IPs&lt;/strong&gt; — an EIP not associated with a running instance is billed hourly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NAT Gateways&lt;/strong&gt; — billed per hour AND per GB; idle ones in dev accounts are a classic leak.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unattached EBS volumes and old snapshots&lt;/strong&gt; — keep getting billed after the instance is long gone.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Idle load balancers and stopped-but-not-terminated dev instances.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; Free-tier traps: the free tier is &lt;strong&gt;12 months and capped&lt;/strong&gt;. NAT Gateways, Elastic IPs, inter-AZ data transfer, and provisioned RDS storage are &lt;em&gt;not&lt;/em&gt; free. A forgotten &lt;code&gt;t2.micro&lt;/code&gt; beyond the cap, or one NAT Gateway left running, quietly bills you for months. Always set a Budget on day one.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Set a $50 monthly budget with an 80% email alert via CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; budget.json &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
{ "BudgetName": "monthly-50usd", "BudgetLimit": {"Amount":"50","Unit":"USD"},
  "TimeUnit": "MONTHLY", "BudgetType": "COST" }
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; notify.json &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
[ { "Notification": {"NotificationType":"ACTUAL","ComparisonOperator":"GREATER_THAN",
      "Threshold":80,"ThresholdType":"PERCENTAGE"},
    "Subscribers":[{"SubscriptionType":"EMAIL","Address":"your@email.com"}] } ]
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;aws budgets create-budget &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--account-id&lt;/span&gt; 123456789012 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--budget&lt;/span&gt; file://budget.json &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--notifications-with-subscribers&lt;/span&gt; file://notify.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Set your very first Budget before launching anything. Add a second alert with &lt;code&gt;NotificationType: FORECASTED&lt;/code&gt; so AWS warns you when it &lt;em&gt;predicts&lt;/em&gt; you'll blow the budget — not just after you already have.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Security Guardrails
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;MFA on the root account&lt;/strong&gt; — then lock the root keys away and never use root for daily work.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Least privilege&lt;/strong&gt; — grant only the permissions a role/user actually needs; start narrow and widen if required.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Never commit access keys&lt;/strong&gt; — no keys in Git, Dockerfiles, or env files in repos. Use a &lt;code&gt;.gitignore&lt;/code&gt; and a secrets scanner in CI.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Roles, not keys&lt;/strong&gt; — EC2/Lambda/ECS use IAM roles for temporary, auto-rotating credentials. Long-lived keys are the #1 breach vector.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Encryption&lt;/strong&gt; — at rest with &lt;strong&gt;KMS&lt;/strong&gt; (enable on S3, EBS, RDS), in transit with TLS everywhere.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CloudTrail + GuardDuty&lt;/strong&gt; — CloudTrail logs every API call (the audit trail); GuardDuty uses ML to flag suspicious behavior (e.g. crypto-mining, anomalous API calls).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security Groups least-open&lt;/strong&gt; — never &lt;code&gt;0.0.0.0/0&lt;/code&gt; on SSH/RDP/DB ports. Reference SGs by ID and open only what's needed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;S3 Block Public Access&lt;/strong&gt; — keep it ON at the account level unless a bucket is deliberately a public website.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; The classic disaster: a developer commits an access key to a public GitHub repo. Bots scan GitHub within &lt;em&gt;minutes&lt;/em&gt; and spin up expensive GPU instances for crypto mining. Use IAM roles instead of keys, and enable a secret scanner so a key never reaches the repo in the first place.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Enable a virtual MFA device for an IAM user:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create the virtual MFA device&lt;/span&gt;
aws iam create-virtual-mfa-device &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--virtual-mfa-device-name&lt;/span&gt; my-mfa &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--outfile&lt;/span&gt; /tmp/qr.png &lt;span class="nt"&gt;--bootstrap-method&lt;/span&gt; QRCodePNG

&lt;span class="c"&gt;# Scan QR in your authenticator app, then enable with two consecutive codes&lt;/span&gt;
aws iam enable-mfa-device &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--user-name&lt;/span&gt; myuser &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--serial-number&lt;/span&gt; arn:aws:iam::123456789012:mfa/my-mfa &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--authentication-code1&lt;/span&gt; 123456 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--authentication-code2&lt;/span&gt; 654321
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Turn ON account-wide S3 public access block&lt;/span&gt;
aws s3control put-public-access-block &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--account-id&lt;/span&gt; 123456789012 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--public-access-block-configuration&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nv"&gt;BlockPublicAcls&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;,IgnorePublicAcls&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;,BlockPublicPolicy&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;,RestrictPublicBuckets&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; A simple priority order for any new account: (1) MFA on root, (2) a Budget alert, (3) CloudTrail on, (4) S3 Block Public Access on, (5) IAM roles instead of keys. Five steps, fifteen minutes, and you've dodged the most common cost and security incidents.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Prefer a day-by-day path? This is covered in &lt;a href="https://opscanopy.com/mission-90/" rel="noopener noreferrer"&gt;&lt;strong&gt;Mission 90 Days 46–65&lt;/strong&gt;&lt;/a&gt; — a free 90-day guided DevOps program with browser terminal missions.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;Running containers on AWS? Continue with &lt;a href="https://opscanopy.com/learn/guides/kubernetes-for-devops/" rel="noopener noreferrer"&gt;Kubernetes for DevOps&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cloud</category>
      <category>devops</category>
      <category>iam</category>
    </item>
    <item>
      <title>How to Validate .gitlab-ci.yml Before You Push</title>
      <dc:creator>opscanopy</dc:creator>
      <pubDate>Tue, 11 Aug 2026 15:12:10 +0000</pubDate>
      <link>https://dev.to/opscanopy/how-to-validate-gitlab-ciyml-before-you-push-374j</link>
      <guid>https://dev.to/opscanopy/how-to-validate-gitlab-ciyml-before-you-push-374j</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fopscanopy.com%2Fblog%2Fvalidate-gitlab-ci-yml-hero.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fopscanopy.com%2Fblog%2Fvalidate-gitlab-ci-yml-hero.svg" alt="GitLab CI validator checking a .gitlab-ci.yml for YAML and pipeline errors before push" width="1200" height="630"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You change one line in &lt;code&gt;.gitlab-ci.yml&lt;/code&gt;, push, and switch to something else. Two minutes later the pipeline goes red — not because the build broke, but because a job points at a &lt;code&gt;stage&lt;/code&gt; you renamed last week. You fix the typo, push again, wait again. This is the loop, and the only way out is to validate &lt;code&gt;.gitlab-ci.yml&lt;/code&gt; &lt;em&gt;before&lt;/em&gt; the commit lands, not after the runner tells you.&lt;/p&gt;

&lt;p&gt;The frustrating part is that GitLab already knows your config is broken the instant it parses it. It just doesn't tell you until you've pushed and burned a CI minute. The fix is to run that same check locally, in the browser, before you ever &lt;code&gt;git push&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The push-and-pray loop
&lt;/h2&gt;

&lt;p&gt;Here's the shape of the problem. You edit a job, push, and let GitLab be your linter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add .gitlab-ci.yml
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"split deploy into staging + prod"&lt;/span&gt;
git push
&lt;span class="c"&gt;# wait for the runner to pick up the pipeline...&lt;/span&gt;
&lt;span class="c"&gt;# pipeline failed: "chosen stage prod does not exist"&lt;/span&gt;
git commit &lt;span class="nt"&gt;-am&lt;/span&gt; &lt;span class="s2"&gt;"fix: declare prod stage"&lt;/span&gt;
git push
&lt;span class="c"&gt;# wait again...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each round trip is a commit you didn't want, a runner slot you didn't need, and a context switch that costs more than the typo did. The errors that cause this almost never need a runner to detect. They're visible the moment the YAML is parsed and the job graph is resolved — which is exactly what a validator does locally.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two kinds of errors: YAML syntax vs structural
&lt;/h2&gt;

&lt;p&gt;When GitLab rejects a pipeline, the failure is one of two categories, and they have completely different fixes.&lt;/p&gt;

&lt;p&gt;The first is a &lt;strong&gt;YAML syntax error&lt;/strong&gt;: the file isn't valid YAML at all, so nothing downstream can read it. The second is a &lt;strong&gt;structural error&lt;/strong&gt;: the YAML parses fine, but the &lt;em&gt;pipeline&lt;/em&gt; it describes is invalid — a job with no script, a stage that was never declared, a &lt;code&gt;needs&lt;/code&gt; pointing at a job that doesn't exist.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# YAML error — the parser can't even build a document&lt;/span&gt;
&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;make&lt;/span&gt;
   &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;make test&lt;/span&gt;      &lt;span class="c1"&gt;# inconsistent indentation: parser bails here&lt;/span&gt;

&lt;span class="c1"&gt;# Structural error — valid YAML, invalid pipeline&lt;/span&gt;
&lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;prod&lt;/span&gt;        &lt;span class="c1"&gt;# "prod" is not in stages: → GitLab refuses to run it&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./deploy.sh&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Valid YAML is only half the job. The &lt;a href="https://opscanopy.com/gitlab-ci-validator/" rel="noopener noreferrer"&gt;GitLab CI Validator&lt;/a&gt; checks both in one pass: it parses the YAML first, and only if that succeeds does it run the structural checks against your jobs. If the parse fails, you get a single line-referenced error and nothing else — there's no point reporting "undefined stage" on a document that didn't parse.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjaeykj23susr7bym6coh.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjaeykj23susr7bym6coh.webp" alt="Illustration: a glowing .gitlab-ci.yml scanned by CI lint tools, yamllint and editor checks, with OK and error verdicts flowing toward a draft merge request" width="799" height="350"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  YAML errors that bite: indentation, tabs, duplicate keys
&lt;/h2&gt;

&lt;p&gt;YAML is whitespace-significant, and CI config is exactly the kind of nested structure where that bites. The classic GitLab error message — &lt;code&gt;did not find expected key&lt;/code&gt; — is almost always one of these.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
    &lt;span class="s"&gt;script&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;              &lt;span class="c1"&gt;# a literal TAB instead of spaces → parse error&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;npm test&lt;/span&gt;

&lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;DEPLOY_ENV&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;staging&lt;/span&gt;
  &lt;span class="na"&gt;DEPLOY_ENV&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;prod&lt;/span&gt;       &lt;span class="c1"&gt;# duplicate key — the first value is silently lost&lt;/span&gt;

&lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nl"&gt;&amp;amp;deploy_steps&lt;/span&gt;  &lt;span class="c1"&gt;# anchor defined...&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./deploy.sh&lt;/span&gt;
&lt;span class="na"&gt;rollback&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*deploy_step&lt;/span&gt;   &lt;span class="c1"&gt;# ...but referenced with a typo → "unknown alias"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A browser validator parses with a real YAML reader, so it reports the exact line where the structure broke. When you paste config and the result is &lt;code&gt;Could not parse YAML: ... (line 4, column 2)&lt;/code&gt;, that's the parser telling you precisely where to look — re-indent, swap the tab for spaces, or fix the anchor name, and re-validate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Structural errors GitLab catches late: undefined stages, jobs with no script, bad needs/extends
&lt;/h2&gt;

&lt;p&gt;These are the ones that make you wait for a runner only to be told the pipeline never started. They're the real reason to validate GitLab CI before push. The validator models the rules from GitLab's &lt;code&gt;.gitlab-ci.yml&lt;/code&gt; keyword reference and flags each one with the offending job, the line, and the fix.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fopscanopy.com%2Fblog%2Fvalidate-gitlab-ci-yml-diagram.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fopscanopy.com%2Fblog%2Fvalidate-gitlab-ci-yml-diagram.svg" alt="A validation pipeline flow: paste .gitlab-ci.yml, parse YAML, run structural checks, then show valid or a list of errors" width="1200" height="620"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A job with no executable surface.&lt;/strong&gt; Every visible job has to &lt;em&gt;do&lt;/em&gt; something: run commands with &lt;code&gt;script:&lt;/code&gt; (or the newer &lt;code&gt;run:&lt;/code&gt;), start a downstream pipeline with &lt;code&gt;trigger:&lt;/code&gt;, or inherit one of those via &lt;code&gt;extends:&lt;/code&gt;. A job with none of them is rejected with the familiar "job config should implement a script: or a trigger: keyword."&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ERROR — empty-job defines no script, run, trigger, or extends&lt;/span&gt;
&lt;span class="na"&gt;empty-job&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
  &lt;span class="c1"&gt;# nothing here → GitLab won't run it&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that an &lt;em&gt;empty&lt;/em&gt; &lt;code&gt;script: []&lt;/code&gt; or &lt;code&gt;script: ""&lt;/code&gt; counts as missing too — the validator treats only a non-empty command string or list as a real executable surface, the same way GitLab does.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A stage that isn't declared.&lt;/strong&gt; If a job's &lt;code&gt;stage:&lt;/code&gt; isn't in your &lt;code&gt;stages:&lt;/code&gt; list (or one of the five defaults: &lt;code&gt;.pre&lt;/code&gt;, &lt;code&gt;build&lt;/code&gt;, &lt;code&gt;test&lt;/code&gt;, &lt;code&gt;deploy&lt;/code&gt;, &lt;code&gt;.post&lt;/code&gt;), GitLab doesn't know when to run it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;stages&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;build&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;

&lt;span class="na"&gt;release-job&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;release&lt;/span&gt;      &lt;span class="c1"&gt;# ERROR — "release" is not in stages:&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;make release&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There's a subtle variant the validator also catches: a job that &lt;em&gt;omits&lt;/em&gt; &lt;code&gt;stage:&lt;/code&gt; defaults to the implicit &lt;code&gt;test&lt;/code&gt; stage. If you declared a custom &lt;code&gt;stages:&lt;/code&gt; list that doesn't include &lt;code&gt;test&lt;/code&gt;, that job is now pointing at a stage you never declared — and GitLab fails with "chosen stage test does not exist."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;needs&lt;/code&gt; / &lt;code&gt;dependencies&lt;/code&gt; / &lt;code&gt;extends&lt;/code&gt; pointing at a job that doesn't exist.&lt;/strong&gt; Every name in &lt;code&gt;needs:&lt;/code&gt;, &lt;code&gt;dependencies:&lt;/code&gt;, or &lt;code&gt;extends:&lt;/code&gt; has to resolve to a real job or hidden &lt;code&gt;.template&lt;/code&gt; in the same file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
  &lt;span class="na"&gt;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;compile&lt;/span&gt;          &lt;span class="c1"&gt;# ERROR — no job named "compile"&lt;/span&gt;
  &lt;span class="na"&gt;extends&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.base&lt;/span&gt;       &lt;span class="c1"&gt;# ERROR — no template named ".base"&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;make test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The validator builds the set of every job id and every &lt;code&gt;.template&lt;/code&gt;, then checks each reference against it. Rename a template and forget to update an &lt;code&gt;extends:&lt;/code&gt;, and it tells you which job broke before the runner does.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;An invalid &lt;code&gt;when:&lt;/code&gt; or a non-list &lt;code&gt;rules:&lt;/code&gt;.&lt;/strong&gt; The &lt;code&gt;when:&lt;/code&gt; keyword only accepts &lt;code&gt;on_success&lt;/code&gt;, &lt;code&gt;on_failure&lt;/code&gt;, &lt;code&gt;always&lt;/code&gt;, &lt;code&gt;manual&lt;/code&gt;, &lt;code&gt;delayed&lt;/code&gt;, or &lt;code&gt;never&lt;/code&gt;. And &lt;code&gt;rules:&lt;/code&gt; has to be a YAML &lt;em&gt;list&lt;/em&gt; of rule objects — a bare mapping is a common mistake that silently changes when a job runs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;deploy&lt;/span&gt;
  &lt;span class="na"&gt;when&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sometimes&lt;/span&gt;      &lt;span class="c1"&gt;# ERROR — not an allowed when value&lt;/span&gt;
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;$CI_COMMIT_TAG'&lt;/span&gt;   &lt;span class="c1"&gt;# ERROR — rules must be a list, not a mapping&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./deploy.sh&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It also surfaces lower-severity advice: legacy &lt;code&gt;only&lt;/code&gt;/&lt;code&gt;except&lt;/code&gt; get an info note recommending &lt;code&gt;rules:&lt;/code&gt; (the two can't be combined in one job), a top-level key that's one edit away from a reserved keyword — say &lt;code&gt;varables:&lt;/code&gt; or &lt;code&gt;beforescript:&lt;/code&gt; — gets a misspelling warning, and malformed &lt;code&gt;image:&lt;/code&gt;/&lt;code&gt;services:&lt;/code&gt; shapes are flagged as errors.&lt;/p&gt;

&lt;h2&gt;
  
  
  Validating before you push: GitLab CI Lint vs an in-browser validator
&lt;/h2&gt;

&lt;p&gt;GitLab ships its own checker — CI Lint, inside the pipeline editor. It's authoritative: it resolves &lt;code&gt;include:&lt;/code&gt; files and project-level CI/CD variables, which a client-side tool can't see. But it has a cost: it requires a project and a sign-in. You can't lint a snippet from a code review, a config you're drafting offline, or a proprietary pipeline you'd rather not paste into a hosted form.&lt;/p&gt;

&lt;p&gt;So what does an in-browser validator actually check? Based on the engine, the flow is deterministic and entirely local:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Parse the YAML.&lt;/strong&gt; Any failure returns a single line-referenced error and stops — no structural findings on an unparseable document.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Split the top level&lt;/strong&gt; into global keywords (&lt;code&gt;stages&lt;/code&gt;, &lt;code&gt;default&lt;/code&gt;, &lt;code&gt;variables&lt;/code&gt;, &lt;code&gt;image&lt;/code&gt;, &lt;code&gt;services&lt;/code&gt;…), visible jobs, and hidden &lt;code&gt;.templates&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resolve the stages&lt;/strong&gt; — your declared &lt;code&gt;stages:&lt;/code&gt; list, or the five defaults — into the set every job's &lt;code&gt;stage:&lt;/code&gt; is checked against.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check every job&lt;/strong&gt; for an executable surface, a known stage, real &lt;code&gt;needs&lt;/code&gt;/&lt;code&gt;extends&lt;/code&gt;/&lt;code&gt;dependencies&lt;/code&gt; targets, a valid &lt;code&gt;when:&lt;/code&gt;, a list-shaped &lt;code&gt;rules:&lt;/code&gt;, and sane &lt;code&gt;image&lt;/code&gt;/&lt;code&gt;services&lt;/code&gt; shapes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rank by severity&lt;/strong&gt; — errors first, then warnings, then info — each with the line and a concrete remediation. It never throws; a parse failure is reported, not crashed.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The honest framing: a clean result in the browser is strong pre-push confidence on &lt;em&gt;structure and syntax&lt;/em&gt;. It catches the entire class of mistakes that fail a pipeline before any job runs. For absolute certainty on a config that uses &lt;code&gt;include:&lt;/code&gt; or project variables, confirm with GitLab's own CI Lint once you've pushed to a project — but use the in-browser pass to make that push count.&lt;/p&gt;

&lt;p&gt;If you also run GitHub Actions, the same idea applies there: the &lt;a href="https://opscanopy.com/github-actions-validator/" rel="noopener noreferrer"&gt;GitHub Actions Validator&lt;/a&gt; finds YAML and security issues in your workflow files, and the &lt;a href="https://opscanopy.com/github-actions-expression-tester/" rel="noopener noreferrer"&gt;GitHub Actions Expression Tester&lt;/a&gt; evaluates those &lt;code&gt;${{ … }}&lt;/code&gt; expressions before you push.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wire it into your workflow
&lt;/h2&gt;

&lt;p&gt;The validator is a paste-and-check tool, but the habit you want is "never push CI config you haven't validated." A pre-commit hook makes that automatic for the YAML half — catch the parse errors before the commit even forms:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="c"&gt;# .git/hooks/pre-commit — block a commit if .gitlab-ci.yml isn't valid YAML&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-euo&lt;/span&gt; pipefail

&lt;span class="k"&gt;if &lt;/span&gt;git diff &lt;span class="nt"&gt;--cached&lt;/span&gt; &lt;span class="nt"&gt;--name-only&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="s1"&gt;'^\.gitlab-ci\.yml$'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
  &lt;span class="c"&gt;# Fail fast on a syntax error before the commit lands.&lt;/span&gt;
  python &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"import sys, yaml; yaml.safe_load(open('.gitlab-ci.yml'))"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"✗ .gitlab-ci.yml is not valid YAML — commit blocked"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"✓ .gitlab-ci.yml parses — paste it into the validator for structural checks"&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A local YAML parse catches the indentation-and-tabs class instantly. For the structural class — undefined stages, broken &lt;code&gt;needs&lt;/code&gt;, jobs with no script — paste the file into the browser validator before you push. The two together cover both error categories from the second section, and neither needs a runner.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# the loop you actually want&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;git add .gitlab-ci.yml          &lt;span class="c"&gt;# pre-commit hook checks YAML&lt;/span&gt;
&lt;span class="c"&gt;# paste .gitlab-ci.yml → validator → 0 errors&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"split deploy into staging + prod"&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;git push                        &lt;span class="c"&gt;# green on the first try&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Validate it now
&lt;/h2&gt;

&lt;p&gt;The next time you touch &lt;code&gt;.gitlab-ci.yml&lt;/code&gt;, don't let the runner be the first thing to read it. Paste the file into the &lt;a href="https://opscanopy.com/gitlab-ci-validator/" rel="noopener noreferrer"&gt;GitLab CI Validator&lt;/a&gt; and you'll get the YAML errors and the structural mistakes — undefined stages, jobs with no script, broken &lt;code&gt;needs&lt;/code&gt;/&lt;code&gt;extends&lt;/code&gt;, invalid &lt;code&gt;when:&lt;/code&gt; — in one pass, with the line and the fix for each. It runs entirely in your browser: no project, no login, and nothing uploaded, so it's safe for internal pipelines.&lt;/p&gt;

&lt;p&gt;If you've ever pushed a CI change and hoped it worked, this is the step that was missing.&lt;/p&gt;

</description>
      <category>gitlabci</category>
      <category>cicd</category>
      <category>yaml</category>
    </item>
    <item>
      <title>Unit Testing Loki Alert Rules: the gap promtool leaves</title>
      <dc:creator>opscanopy</dc:creator>
      <pubDate>Tue, 11 Aug 2026 15:11:36 +0000</pubDate>
      <link>https://dev.to/opscanopy/unit-testing-loki-alert-rules-the-gap-promtool-leaves-eee</link>
      <guid>https://dev.to/opscanopy/unit-testing-loki-alert-rules-the-gap-promtool-leaves-eee</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fopscanopy.com%2Fblog%2Funit-testing-loki-alert-rules-hero.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fopscanopy.com%2Fblog%2Funit-testing-loki-alert-rules-hero.svg" alt="Unit testing Loki alert rules: a promtool-style test loop for LogQL alerting rules" width="1200" height="630"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you run Prometheus, you already have a safety net for your alerting logic: &lt;code&gt;promtool test rules&lt;/code&gt;. You feed it a series of synthetic samples, declare what should fire and when, and CI tells you the moment a refactor breaks an alert. It’s the difference between catching a broken page rule in code review and discovering it during an incident.&lt;/p&gt;

&lt;p&gt;Grafana Loki has no equivalent. You can write LogQL alerting and recording rules that look almost identical to their Prometheus cousins, load them into the ruler, and ship them — but there’s no first-class way to assert that a given stream of logs produces the alert you expect. The gap is real, it’s long-standing, and it’s exactly the kind of thing that bites you at 3&amp;nbsp;a.m.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why promtool does not cover Loki
&lt;/h2&gt;

&lt;p&gt;The instinctive move is to reach for &lt;code&gt;promtool&lt;/code&gt; and point it at your Loki rules. It doesn’t work, and the reason is fundamental rather than cosmetic.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;promtool test rules&lt;/code&gt; evaluates PromQL against a synthetic &lt;strong&gt;time series&lt;/strong&gt; database. You describe metrics with the &lt;code&gt;series&lt;/code&gt;/&lt;code&gt;values&lt;/code&gt; syntax and the tool replays them through the rule engine. But a Loki alert rule doesn’t start from metrics — it starts from &lt;strong&gt;log lines&lt;/strong&gt;. A rule like &lt;code&gt;count_over_time({app="api"} |= "panic" [5m]) &amp;gt; 0&lt;/code&gt; has to run a LogQL pipeline (stream selector, line filter, label extraction, then a metric aggregation) over raw log entries before there’s any series to evaluate. promtool has no concept of a log stream, no LogQL parser, and no way to materialise the intermediate metrics the way Loki’s query engine does. Feeding it Loki rules either errors out or silently tests the wrong thing.&lt;/p&gt;

&lt;p&gt;So the test surface that matters for Loki — “given these log lines, does this LogQL rule fire?” — is precisely the surface promtool can’t reach.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fopscanopy.com%2Fblog%2Funit-testing-loki-alert-rules-diagram.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fopscanopy.com%2Fblog%2Funit-testing-loki-alert-rules-diagram.svg" alt="A Loki alert rule unit test loop: synthetic log streams evaluated at a chosen time and asserted against the expected alerts" width="1200" height="640"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this matters
&lt;/h2&gt;

&lt;p&gt;LogQL alert rules are deceptively easy to get subtly wrong:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A line filter that matches more (or less) than you think because of an unescaped regex or a missing word boundary.&lt;/li&gt;
&lt;li&gt;A label that you &lt;code&gt;unwrap&lt;/code&gt; or &lt;code&gt;label_format&lt;/code&gt; incorrectly, so the aggregation groups the wrong way.&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;[5m]&lt;/code&gt; range and a &lt;code&gt;for: 10m&lt;/code&gt; clause that interact so the alert never has enough data to fire, or fires far later than intended.&lt;/li&gt;
&lt;li&gt;A recording rule whose output series silently changes labels after a pipeline edit, breaking every downstream alert that selects on it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of these are caught by YAML linting or a schema check. They’re &lt;strong&gt;behavioural&lt;/strong&gt; bugs, and the only honest way to catch them is to run the rule against representative input and assert on the output. Without a test harness, that verification happens manually, infrequently, and usually after something has already paged the wrong team — or failed to page the right one.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a Loki rule unit test should look like
&lt;/h2&gt;

&lt;p&gt;The model promtool established is the right one; it just needs a log-shaped input. Instead of synthetic series, a Loki rule test should accept synthetic &lt;strong&gt;streams&lt;/strong&gt; (a set of labels plus timestamped log lines), evaluate the rule at a chosen time, and assert on the alerts produced — something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# loki-rule-tests.yaml&lt;/span&gt;
&lt;span class="na"&gt;tests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;panic in api logs fires PanicDetected&lt;/span&gt;
    &lt;span class="c1"&gt;# Synthetic log streams replayed through the LogQL engine.&lt;/span&gt;
    &lt;span class="na"&gt;input_streams&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;{app="api",&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;env="prod"}'&lt;/span&gt;
        &lt;span class="na"&gt;entries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;ts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2026-06-08T10:00:30Z"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;line&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;level=info&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;msg=ok"&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;ts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2026-06-08T10:01:10Z"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;line&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;level=error&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;msg=panic:&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;nil&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;map"&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;ts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2026-06-08T10:02:40Z"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;line&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;level=error&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;msg=panic:&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;nil&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;map"&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;# Evaluate the rule group at this instant.&lt;/span&gt;
    &lt;span class="na"&gt;eval_time&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;2026-06-08T10:05:00Z&lt;/span&gt;

    &lt;span class="na"&gt;alert_rule_test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;alertname&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PanicDetected&lt;/span&gt;
        &lt;span class="c1"&gt;# What we expect the ruler to emit at eval_time.&lt;/span&gt;
        &lt;span class="na"&gt;exp_alerts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;exp_labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;api&lt;/span&gt;
              &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;prod&lt;/span&gt;
              &lt;span class="na"&gt;severity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;critical&lt;/span&gt;
            &lt;span class="na"&gt;exp_annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;summary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Panic&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;detected&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;in&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;api"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The rule under test is the same rule you ship to the ruler:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;groups&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;api-alerts&lt;/span&gt;
    &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;alert&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PanicDetected&lt;/span&gt;
        &lt;span class="na"&gt;expr&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;count_over_time({app="api", env="prod"} |= "panic" [5m]) &amp;gt; 1&lt;/span&gt;
        &lt;span class="na"&gt;for&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0m&lt;/span&gt;
        &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;severity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;critical&lt;/span&gt;
        &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;summary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Panic&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;detected&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;in&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;$labels.app&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read together, the test says: given two panic lines in the five-minute window before &lt;code&gt;10:05&lt;/code&gt;, the &lt;code&gt;count_over_time(...) &amp;gt; 1&lt;/code&gt; expression should be true, and the ruler should emit a &lt;code&gt;PanicDetected&lt;/code&gt; alert carrying &lt;code&gt;severity=critical&lt;/code&gt; and the &lt;code&gt;app&lt;/code&gt;/&lt;code&gt;env&lt;/code&gt; labels from the stream. Flip the input to a single panic line, or move one entry outside the &lt;code&gt;[5m]&lt;/code&gt; window, and &lt;code&gt;exp_alerts&lt;/code&gt; becomes empty — the test now guards both the firing and the not-firing case.&lt;/p&gt;

&lt;p&gt;This is the shape every team that has asked for it on the Loki tracker keeps describing — see the long-running requests in Loki issues &lt;a href="https://github.com/grafana/loki/issues/7655" rel="noopener noreferrer"&gt;#7655&lt;/a&gt; and &lt;a href="https://github.com/grafana/loki/issues/16659" rel="noopener noreferrer"&gt;#16659&lt;/a&gt;, where the community has repeatedly pointed out that a promtool-style unit test for LogQL rules simply doesn’t exist yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing the gap today
&lt;/h2&gt;

&lt;p&gt;You don’t have to wait for upstream to ship this. &lt;strong&gt;AlertLint&lt;/strong&gt; runs exactly this test loop in your browser: paste your Loki alerting and recording rules, define &lt;code&gt;input_streams&lt;/code&gt;, declare your &lt;code&gt;exp_alerts&lt;/code&gt;, and assert pass or fail before the rule ever reaches the ruler. Everything evaluates client-side — your rules and logs never leave the device — so you can wire it into review without touching infrastructure or sending data anywhere.&lt;/p&gt;

&lt;p&gt;If you’ve ever shipped a Loki alert and hoped it worked, this is the missing step.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://opscanopy.com/loki-alert-rule-tester/" rel="noopener noreferrer"&gt;Try AlertLint — the Loki alert rule tester →&lt;/a&gt;&lt;/p&gt;

</description>
      <category>loki</category>
      <category>observability</category>
      <category>testing</category>
    </item>
  </channel>
</rss>
