Why Networking Matters
Real infrastructure involves multiple servers communicating over networks. Vagrant lets you simulate complex network topologies locally — perfect for testing before deploying to production.
Docker Network Types
| Type | Description | Use Case |
|---|---|---|
bridge | Default isolated network | Container-to-container communication |
host | Shares host network stack | Performance-critical apps |
overlay | Multi-host networking | Docker Swarm/clusters |
none | No networking | Fully isolated containers |
Port Forwarding
Port forwarding maps a port on your host machine to a port inside the container.
lab.vm.network "forwarded_port", guest: 80, host: 8080
lab.vm.network "forwarded_port", guest: 443, host: 8443This means:
- Traffic to
localhost:8080→ forwarded to container port80 - Traffic to
localhost:8443→ forwarded to container port443
Handling Port Collisions
If port 8080 is already in use, Vagrant can auto-correct:
lab.vm.network "forwarded_port", guest: 80, host: 8080, auto_correct: trueDefine a custom range for auto-correction:
lab.vm.usable_port_range = (9000..9100)Practical Example: Web Server
ENV['VAGRANT_DEFAULT_PROVIDER'] = 'docker'
Vagrant.configure("2") do |config|
config.vm.define "web_server" do |web|
web.vm.network "forwarded_port", guest: 80, host: 9000, auto_correct: true
web.vm.hostname = "webserver"
web.vm.provider "docker" do |docker|
docker.image = "nextgenplayground:vagrant"
docker.has_ssh = true
docker.privileged = true
docker.create_args = ["-v", "/sys/fs/cgroup:/sys/fs/cgroup:ro"]
docker.name = "web_server"
end
end
endAfter vagrant up, install nginx inside:
vagrant ssh
sudo apt-get install nginx -y
sudo systemctl enable --now nginx
exitNow curl http://localhost:9000 returns the nginx welcome page from your host.
Private Networks
Private networks create isolated communication between containers — invisible to the outside world.
Static IP
web.vm.network "private_network", ip: "192.168.10.10", netmask: 24DHCP (Automatic IP)
web.vm.network "private_network", type: "dhcp"Public Networks
Public (bridged) networks put containers on the same network as your host:
web.vm.network "public_network", type: "dhcp"
# or with static IP:
web.vm.network "public_network", ip: "192.168.1.100"Setting Hostnames
web.vm.hostname = "my-server"This sets the container's hostname and updates /etc/hosts.
Resource Allocation
Control CPU and memory per container:
docker.create_args = ['--cpuset-cpus=1'] # 1 vCPU
docker.create_args = ['--memory=1g'] # 1GB RAMCombine multiple args:
docker.create_args = [
"-v", "/sys/fs/cgroup:/sys/fs/cgroup:ro",
"--cpuset-cpus=1",
"--memory=1g"
]Multi-Container Architecture
Here's a real-world example — three containers across two networks:
ENV['VAGRANT_DEFAULT_PROVIDER'] = 'docker'
Vagrant.configure("2") do |config|
# Container 1: Frontend (Network A)
config.vm.define "frontend" do |node|
node.vm.network "private_network", ip: "192.168.1.10", netmask: 24
node.vm.network "forwarded_port", guest: 80, host: 8080
node.vm.hostname = "frontend"
node.vm.provider "docker" do |docker|
docker.image = "nextgenplayground:vagrant"
docker.has_ssh = true
docker.privileged = true
docker.create_args = ["-v", "/sys/fs/cgroup:/sys/fs/cgroup:ro", "--memory=1g", "--cpuset-cpus=1"]
docker.name = "frontend"
end
end
# Container 2: API Gateway (Network A + Network B)
config.vm.define "api" do |node|
node.vm.network "private_network", ip: "192.168.1.11", netmask: 24
node.vm.network "private_network", ip: "192.168.2.11", netmask: 24
node.vm.hostname = "api-gateway"
node.vm.provider "docker" do |docker|
docker.image = "nextgenplayground:vagrant"
docker.has_ssh = true
docker.privileged = true
docker.create_args = ["-v", "/sys/fs/cgroup:/sys/fs/cgroup:ro", "--memory=1g", "--cpuset-cpus=1"]
docker.name = "api"
end
end
# Container 3: Database (Network B only)
config.vm.define "database" do |node|
node.vm.network "private_network", ip: "192.168.2.10", netmask: 24
node.vm.hostname = "database"
node.vm.provider "docker" do |docker|
docker.image = "nextgenplayground:vagrant"
docker.has_ssh = true
docker.privileged = true
docker.create_args = ["-v", "/sys/fs/cgroup:/sys/fs/cgroup:ro", "--memory=1g", "--cpuset-cpus=1"]
docker.name = "database"
end
end
endNetwork Topology
┌─────────────────────────────────────────────────┐
│ Host Machine │
│ │
│ ┌─── Network A (192.168.1.0/24) ───┐ │
│ │ │ │
│ │ ┌──────────┐ ┌──────────┐ │ │
│ │ │ frontend │ │ api │ │ │
│ │ │ .1.10 │ │ .1.11 │ │ │
│ │ └──────────┘ └────┬─────┘ │ │
│ │ │ │ │
│ └───────────────────────┼───────────┘ │
│ │ │
│ ┌─── Network B (192.168.2.0/24) ───┐ │
│ │ │ │ │
│ │ ┌────┴─────┐ │ │
│ │ │ api │ │ │
│ │ │ .2.11 │ │ │
│ │ └──────────┘ │ │
│ │ │ │
│ │ ┌──────────┐ │ │
│ │ │ database │ │ │
│ │ │ .2.10 │ │ │
│ │ └──────────┘ │ │
│ └───────────────────────────────────┘ │
└─────────────────────────────────────────────────┘
Testing Connectivity
# Connect to frontend
vagrant ssh frontend
# Can reach API on Network A
ping 192.168.1.11 # ✓ Success
# Cannot reach database (different network)
ping 192.168.2.10 # ✗ No response
exit
# Connect to API (has both networks)
vagrant ssh api
ping 192.168.1.10 # ✓ Reaches frontend
ping 192.168.2.10 # ✓ Reaches databaseUsing Loops for Scalable Configs
Instead of repeating configuration for similar containers, use Ruby loops:
ENV['VAGRANT_DEFAULT_PROVIDER'] = 'docker'
nodes = 4
(1..nodes).each do |i|
Vagrant.configure("2") do |config|
config.vm.define "node#{i}" do |node|
node.vm.network "private_network", ip: "192.168.10.1#{i}", netmask: 24
node.vm.hostname = "node#{i}"
node.vm.provider "docker" do |docker|
docker.image = "nextgenplayground:vagrant"
docker.has_ssh = true
docker.privileged = true
docker.create_args = ["-v", "/sys/fs/cgroup:/sys/fs/cgroup:ro"]
docker.name = "node#{i}"
end
end
end
endThis creates 4 containers (node1 through node4) with IPs 192.168.10.11 through 192.168.10.14 — in just 15 lines instead of 60+.
How the Loop Works
nodes = 4— Number of containers to create(1..nodes).each do |i|— Loop from 1 to 4"node#{i}"— Ruby string interpolation (node1, node2, etc.)"192.168.10.1#{i}"— Dynamic IP addresses
Combining All Network Options
config.vm.define "server" do |srv|
srv.vm.hostname = "my-server"
srv.vm.network "forwarded_port", guest: 80, host: 9000, auto_correct: true
srv.vm.network "private_network", ip: "192.168.10.10", netmask: 24
srv.vm.network "public_network", type: "dhcp"
srv.vm.usable_port_range = (5000..5100)
srv.vm.provider "docker" do |docker|
docker.image = "nextgenplayground:vagrant"
docker.has_ssh = true
docker.privileged = true
docker.create_args = ["-v", "/sys/fs/cgroup:/sys/fs/cgroup:ro", "--memory=2g", "--cpuset-cpus=2"]
docker.name = "server"
end
endCleanup
Always clean up when done:
# Destroy specific environment
vagrant destroy -f
# Nuclear option: remove ALL Docker containers
docker rm -f $(docker ps -a -q)Summary
- Port forwarding maps host ports to container ports
- Private networks isolate communication between containers
- Public networks bridge containers to your LAN
- Resource allocation controls CPU and memory per container
- Multi-container architectures simulate real infrastructure
- Loops make Vagrantfiles scalable and maintainable
- Network isolation ensures containers only communicate as designed
Practice Exercise
Deploy an architecture with:
- A
webcontainer on network192.168.1.0/24with IP.10, port 80 forwarded to host port 9000 - An
appcontainer on both192.168.1.0/24(IP.11) and192.168.2.0/24(IP.11) - A
dbcontainer on network192.168.2.0/24with IP.10
Verify that web can reach app but NOT db, and that app can reach both.
Next Steps
You now have the skills to design and deploy complex local environments with Vagrant. As you continue through the DevOps track, you'll use these skills to test Docker, Kubernetes, and CI/CD configurations locally before deploying to production.