Network Engineering for DevOps Teams: What You Need to Know
There is a gap in many DevOps teams. Engineers who can build sophisticated CI/CD pipelines and orchestrate containers across clusters sometimes struggle when a network issue appears. Packets get dropped, DNS resolution fails intermittently, services cannot reach each other across VPCs, and suddenly the team is waiting for “the network person” to diagnose it.
This post bridges that gap. We will walk through the networking concepts that every DevOps engineer should understand — not at CCIE depth, but deep enough to design, debug, and automate network infrastructure alongside the rest of the stack.
graph TD
INET[Internet] --> FW[Firewall / WAF]
FW --> LB[Load Balancer]
LB --> APP[App Servers - Private Subnet]
APP --> DB[(Database - Isolated Subnet)]
APP --> MQ[Cache / MQ - Private Subnet]
LB ~~~ VPC
subgraph VPC[VPC 10.0.0.0/16]
APP
DB
MQ
end
VPC and VLAN Design: The Foundation
Whether you are working in AWS, Azure, or on bare metal with Proxmox (which we covered in our private cloud post), network segmentation is the starting point.
In public cloud, a VPC (Virtual Private Cloud) gives you an isolated network. The critical design decisions are:
- CIDR block sizing — Choose a block large enough for growth but small enough to avoid overlap with other VPCs or on-premises networks. A
/16(65,536 IPs) per environment is common. - Subnet strategy — Separate public subnets (with internet gateway routes) from private subnets. Place load balancers in public subnets, application workloads in private subnets, and databases in isolated subnets with no internet route.
- Availability zone distribution — Spread subnets across AZs for resilience.
A typical Terraform VPC layout:
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "~> 5.0"
name = "production"
cidr = "10.0.0.0/16"
azs = ["eu-central-1a", "eu-central-1b", "eu-central-1c"]
private_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
public_subnets = ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24"]
enable_nat_gateway = true
single_nat_gateway = false # one per AZ for HA
one_nat_gateway_per_az = true
enable_dns_hostnames = true
enable_dns_support = true
}
On bare metal or private cloud, VLANs serve a similar purpose. A typical VLAN scheme:
| VLAN ID | Purpose | CIDR |
|---|---|---|
| 10 | Management / IPMI | 10.10.0.0/24 |
| 20 | Production workloads | 10.20.0.0/22 |
| 30 | Storage (Ceph) | 10.30.0.0/24 |
| 40 | Kubernetes pod network | 10.40.0.0/16 |
| 50 | DMZ / Public-facing | 10.50.0.0/24 |
The key principle is the same in both worlds: segment by trust level and traffic pattern, not by team or application name.
DNS Strategy: More Important Than You Think
DNS is the most underappreciated piece of infrastructure. A solid DNS strategy provides service discovery, enables zero-downtime deployments, and simplifies multi-environment routing.
Internal DNS zones — Every environment should have a private DNS zone (e.g., prod.internal, staging.internal). In AWS, use Route 53 private hosted zones. On-premises, run CoreDNS or BIND as your internal resolver.
Split-horizon DNS — The same domain name resolves differently depending on where the query originates. Internal clients get private IPs; external clients get public IPs.
TTLs matter — Before a migration or failover, lower TTLs to 60 seconds. After the change stabilizes, raise them back to 300-3600 seconds to reduce DNS query volume.
DNS as code — Manage DNS records in Terraform or a GitOps workflow, never by clicking in a console:
resource "aws_route53_record" "api" {
zone_id = aws_route53_zone.internal.zone_id
name = "api.prod.internal"
type = "A"
alias {
name = aws_lb.api.dns_name
zone_id = aws_lb.api.zone_id
evaluate_target_health = true
}
}
Load Balancing: Layers and Patterns
Understanding the difference between L4 and L7 load balancing is essential:
- L4 (TCP/UDP) — Forwards packets based on IP and port. Fast, simple, protocol-agnostic. Use for databases, gRPC, non-HTTP services. AWS NLB, HAProxy in TCP mode.
- L7 (HTTP/HTTPS) — Inspects HTTP headers, paths, cookies. Enables path-based routing, header injection, rate limiting. Use for web applications and APIs. AWS ALB, Nginx, Traefik.
In Kubernetes, this maps to:
- Service type: LoadBalancer — Provisions an L4 load balancer (usually cloud-provider NLB)
- Ingress / Gateway API — L7 routing with rules for host, path, headers
A common mistake is using an L7 load balancer for everything. L7 inspection adds latency and limits throughput. For high-throughput internal services (database replicas, message queues), L4 is the right choice.
Firewall Rules as Code
Traditional firewalls are configured through web GUIs, and changes are tracked in spreadsheets or ticketing systems. This does not scale and it does not integrate with DevOps workflows.
The modern approach: firewall rules as code, version-controlled and applied through CI/CD.
In cloud environments, security groups and network ACLs are already defined in Terraform. On-premises, tools like nftables can be templated and deployed with Ansible:
# ansible role: firewall/tasks/main.yml
- name: Deploy nftables rules
ansible.builtin.template:
src: nftables.conf.j2
dest: /etc/nftables.conf
owner: root
mode: '0600'
notify: restart nftables
- name: Ensure nftables is running
ansible.builtin.systemd:
name: nftables
state: started
enabled: true
# templates/nftables.conf.j2
table inet filter {
chain input {
type filter hook input priority 0; policy drop;
# Allow established connections
ct state established,related accept
# Allow loopback
iif lo accept
# Allow SSH from management VLAN only
ip saddr 10.10.0.0/24 tcp dport 22 accept
# Allow HTTP/HTTPS from DMZ
ip saddr 10.50.0.0/24 tcp dport { 80, 443 } accept
{% for rule in custom_firewall_rules %}
{{ rule }}
{% endfor %}
# Log and drop everything else
log prefix "nftables-drop: " drop
}
}
This approach gives you version history, peer review, and automated testing of firewall changes — the same workflow you use for application code. For a deeper dive into security automation, see our Kubernetes security hardening post.
Service Mesh: When You Need It (and When You Do Not)
A service mesh (Istio, Linkerd, Cilium) adds a proxy sidecar to every pod, giving you mutual TLS, traffic management, and observability without changing application code. The benefits are real:
- mTLS everywhere — Encrypted service-to-service communication with automatic certificate rotation
- Traffic splitting — Canary deployments at the network layer
- Observability — Request-level metrics, distributed tracing, access logs
But the cost is also real: increased resource consumption (each sidecar uses CPU and memory), added latency (every request goes through two proxies), and significant operational complexity.
Our recommendation: Start without a service mesh. Use Kubernetes NetworkPolicies for segmentation and cert-manager for TLS. Only add a service mesh when you have a concrete need — usually mTLS compliance requirements or sophisticated traffic management — and the team capacity to operate it.
Network Observability
You cannot troubleshoot what you cannot see. A minimal network observability stack includes:
- Flow logs — AWS VPC Flow Logs, or
conntrackandtcpdumpon bare metal. These tell you what is talking to what. - DNS query logging — Log and monitor DNS queries to catch misconfigurations and identify services calling unexpected endpoints.
- Latency monitoring — Track p50/p95/p99 latency between services. Tools like Prometheus with
blackbox_exporteror Cilium Hubble. - Packet capture capability — Have
tcpdumpand Wireshark skills on the team. When things get weird, there is no substitute for reading packets.
A quick Prometheus alert for detecting network issues:
groups:
- name: network
rules:
- alert: HighDNSLatency
expr: histogram_quantile(0.95, rate(dns_lookup_duration_seconds_bucket[5m])) > 0.1
for: 5m
labels:
severity: warning
annotations:
summary: "DNS resolution p95 latency above 100ms"
- alert: InterfaceErrors
expr: rate(node_network_receive_errs_total[5m]) > 0
for: 2m
labels:
severity: critical
annotations:
summary: "Network interface {{ $labels.device }} is receiving errors"
Debugging Checklist
When a network issue hits, work through the layers systematically:
- Is the host up? —
ping, check if the machine is reachable at all - Is the port open? —
nc -zv host portorss -tlnpon the target - Is DNS resolving correctly? —
digornslookup, check for stale records - Is traffic being blocked? — Check security groups, nftables, NetworkPolicies
- Is the route correct? —
traceroute, check routing tables, VPC route tables - Is the application listening? —
curl -v, check logs for bind errors
This is methodical, not magical. The most common “network problems” turn out to be DNS caching, misconfigured security groups, or applications binding to localhost instead of 0.0.0.0.
Conclusion
Networking is not a separate discipline from DevOps — it is a core part of it. Every deployment, every service communication, every security boundary relies on the network. By treating network configuration as code, understanding the fundamentals of segmentation and routing, and investing in observability, DevOps teams can own the full stack confidently.
If your team is building a hybrid cloud architecture, the networking layer is where the complexity lives. Getting it right from the start saves months of debugging later.