You’ll see CIDR notation everywhere in server work: 10.0.0.0/24 in a VPN config, 203.0.113.10/32 in a firewall rule, 0.0.0.0/0 meaning “everything”, /48 IPv6 allocations. Once you can read it, firewall rules, private networks and IP allocations make sense at a glance.
What CIDR means
An IPv4 address has 32 bits. A CIDR prefix like /24 says how many leading bits identify the network. The remaining bits identify hosts within it.
192.168.1.0/24
└── first 24 bits = network (192.168.1)
last 8 bits = hosts (0–255)
The same thing as a subnet mask: /24 = 255.255.255.0.
Cheat sheet
| CIDR | Subnet mask | Addresses | Usable hosts* | Typical use |
|---|---|---|---|---|
| /32 | 255.255.255.255 | 1 | 1 (single host) | One IP in firewall rules, a server’s own address |
| /31 | 255.255.255.254 | 2 | 2 | Point-to-point links |
| /30 | 255.255.255.252 | 4 | 2 | Small links |
| /29 | 255.255.255.248 | 8 | 6 | Small IP blocks from providers |
| /28 | 255.255.255.240 | 16 | 14 | |
| /27 | 255.255.255.224 | 32 | 30 | |
| /26 | 255.255.255.192 | 64 | 62 | |
| /25 | 255.255.255.128 | 128 | 126 | |
| /24 | 255.255.255.0 | 256 | 254 | Classic LAN, VPN subnet, smallest globally routed block |
| /23 | 255.255.254.0 | 512 | 510 | |
| /22 | 255.255.252.0 | 1,024 | 1,022 | |
| /20 | 255.255.240.0 | 4,096 | 4,094 | |
| /16 | 255.255.0.0 | 65,536 | 65,534 | Large private networks, Docker defaults |
| /8 | 255.0.0.0 | 16,777,216 | — | 10.0.0.0/8 private range |
| /0 | 0.0.0.0 | everything | — | “Any address” in firewalls and routes |
*In a traditional subnet, the first address is the network address, and the last is the broadcast address, so usable hosts = addresses − 2.
Formula: addresses = 2^(32 − prefix). A /26 has 2^6 = 64.
Network, broadcast and range
For 192.168.10.77/26:
- block size = 64, so networks start at .0, .64, .128, .192;
- .77 falls in the .64 block;
- network:
192.168.10.64, broadcast:192.168.10.127, hosts:.65–.126.
Let a tool do it:
sudo apt install ipcalc
ipcalc 192.168.10.77/26
Python works too: python3 -c "import ipaddress as i; n=i.ip_network('192.168.10.77/26', strict=False); print(n, n.num_addresses, list(n.hosts())[0], list(n.hosts())[-1])".
Splitting a network
A /24 can be split into two /25s, four /26s, eight /27s, and so on. Each extra bit halves the size:
10.0.0.0/24
├── 10.0.0.0/25 (.0–.127)
└── 10.0.0.128/25 (.128–.255)
When planning private networks (VPNs, Docker networks, VPCs), choose ranges that don’t overlap with other networks you connect to. Overlapping subnets are the classic cause of “the VPN works but I can’t reach anything”. See private IP ranges.
CIDR in server configs
Firewall rules:
sudo ufw allow from 203.0.113.50/32 to any port 22 # one IP
sudo ufw allow from 10.0.0.0/24 to any port 5432 # whole private subnet
Nginx allow/deny:
allow 10.0.0.0/24;
allow 203.0.113.50;
deny all;
WireGuard AllowedIPs:
AllowedIPs = 10.8.0.2/32 # just this peer
AllowedIPs = 0.0.0.0/0, ::/0 # route everything through the tunnel
Interface addresses: ip addr add 10.0.0.5/24 dev eth1 sets the address and tells the kernel that 10.0.0.0/24 is directly reachable on eth1. See ip command.
Database access: PostgreSQL pg_hba.conf lines like host all all 10.0.0.0/24 scram-sha-256. See PostgreSQL remote connections.
IPv6 prefixes
Same idea, 128 bits:
| Prefix | Typical meaning |
|---|---|
| /128 | Single address |
| /64 | One network (LAN, or a VPS’s subnet). Required for SLAAC |
| /56 | Typical home or small site allocation (256 /64s) |
| /48 | Typical organisation allocation, and the smallest globally routed block |
Providers often give a VPS a /64 or a single /128. See IPv4 vs IPv6.
Common mistakes
- Writing a host address with a network prefix where a network is expected (
10.0.0.5/24vs10.0.0.0/24). Some tools accept it, others reject it. 0.0.0.0/0in an allow rule when you meant one address.- Overlapping VPN, Docker and office subnets.
- Forgetting IPv6 in firewall allow-lists.
Related: NAT explained and UFW.