Tuxvador's Blog
Automated Daily CrowdSec Reports via Email Managing DNS Programmatically with the IONOS API Getting Started with Proxmox VE for Your Home Lab My Proxmox VE Homelab: Infrastructure Overview Monitoring Your Homelab with Grafana, Loki, and CrowdSec

Setting Up ISC DHCP for Private IPs in a Proxmox Homelab


If you run a Proxmox VE homelab with multiple LXC containers and VMs, you quickly run into the problem of IP management. You could rely on your router’s DHCP, but that ties your infrastructure to whatever upstream device handles your network. In a lab environment, you want full control — static reservations for infrastructure containers, a dynamic pool for ephemeral test VMs, and no dependency on external services like a home router’s DHCP server.

This post walks through setting up ISC DHCP Server in a dedicated LXC container on Proxmox VE, using a representative homelab configuration.

Why a Dedicated DHCP Container?

There are a few approaches to DHCP in a Proxmox lab:

  • Let your upstream router handle it — simple, but you lose control. You can’t define static reservations without logging into your home router, and any router reboot or replacement means reconfiguring everything.
  • Run DHCP on the Proxmox host itself — works, but mixes host OS concerns with application services. You want your hypervisor to stay lean.
  • Dedicated LXC container — clean separation, easy to back up, migrate, or rebuild. This is the way.

A DHCP container is lightweight (we’re talking ~3 MB RAM), has one job, and can be firewalled off from everything except the broadcast domain it serves.

Creating the Container

I create my containers using a consistent naming scheme. This container is called dhcp-srv with the static IP 10.99.99.2/24.

Prerequisites

  • Proxmox VE host with a bridge (typically vmbr0 or vmbr1)
  • Debian LXC template downloaded (in my case, Debian 13 Trixie)
  • SSH public key injected at container creation time (no password auth needed)

Container Creation via CLI

pct create 200 \
  local:vztmpl/debian-13-standard_13.0-1_amd64.tar.zst \
  --hostname dhcp-srv \
  --net0 name=eth0,bridge=vmbr0,ip=dhcp \
  --storage local-lvm \
  --password <temporary-password> \
  --unprivileged 1 \
  --ssh-public-keys ~/.ssh/id_ed25519.pub

The --ssh-public-keys parameter is key — it injects your SSH key during template extraction so you can SSH in immediately after creation without password-based auth.

Important: Debian 13 (Trixie) templates don’t include sudo by default. Step into the container afterward and set it up:

pct enter 200
apt update && apt install -y sudo
echo '%sudo ALL=(ALL:ALL) ALL' >> /etc/sudoers
usermod -aG sudo tuxvador
exit

Setting a Static IP

Once the container boots, set a static IP by editing the network config. Inside the container:

cat > /etc/network/interfaces << 'EOF'
auto lo
iface lo inet loopback

auto eth0
iface eth0 inet static
    address 10.99.99.2/24
    gateway 10.99.99.1
EOF

systemctl restart networking

On the Proxmox host, 10.99.99.1 is the gateway configured on vmbr0 — the upstream router that connects the lab to the internet. The container’s own static IP is 10.99.99.2, which is also what we’ll reserve in the DHCP config itself (yes, DHCP servers need their own static address — that’s configured at the OS level, not via DHCP).

Installing ISC DHCP Server

ISC DHCP Server is the classic, battle-tested DHCP implementation for Linux. It’s available in Debian’s main repository:

apt update && apt install -y isc-dhcp-server

During installation you’ll be prompted to configure the listening interface. We’ll do that manually instead.

Configuring the Listening Interface

Edit /etc/default/isc-dhcp-server to tell the daemon which interface to listen on:

INTERFACESv4="eth0"
INTERFACESv6=""

This is critical — if you leave this blank or point it to the wrong interface, the server won’t respond to DISCOVER messages.

Writing the Configuration

This is the meat of it. The configuration lives at /etc/dhcp/dhcpd.conf. Here’s a representative file:

cat > /etc/dhcp/dhcpd.conf << 'EOF'
option domain-name "home.lab";
option domain-name-servers 8.8.8.8, 1.1.1.1;

default-lease-time 86400;
max-lease-time 86400;

ddns-update-style none;
authoritative;

subnet 10.99.99.0 netmask 255.255.255.0 {
  range 10.99.99.80 10.99.99.200;
  option routers 10.99.99.1;
  option subnet-mask 255.255.255.0;
  option broadcast-address 10.99.99.255;
}

host dhcp-srv      { hardware ethernet 00:1a:2b:a3:ec:58; fixed-address 10.99.99.2;  }
host mon-srv      { hardware ethernet 00:1a:2b:35:9e:14; fixed-address 10.99.99.10; }
host web-proxy         { hardware ethernet 00:1a:2b:e2:df:11; fixed-address 10.99.99.11; }
host media-srv         { hardware ethernet 00:1a:2b:55:0c:64; fixed-address 10.99.99.13; }
host file-srv    { hardware ethernet 00:1a:2b:94:00:9c; fixed-address 10.99.99.14; }
host dev-srv          { hardware ethernet 00:1a:2b:41:af:0a; fixed-address 10.99.99.20; }
host vpn-srv { hardware ethernet 00:1a:2b:53:0c:f5; fixed-address 10.99.99.25; }
EOF

Breaking Down the Configuration

Global Options:

DirectiveValuePurpose
option domain-namehome.labSets the DNS search domain for all clients
option domain-name-servers8.8.8.8, 1.1.1.1DNS servers handed to clients
default-lease-time86400 (24h)How long a lease lasts by default
max-lease-time86400 (24h)Maximum lease duration a client can request
ddns-update-stylenoneDisables DDNS updates (not needed in a lab)
authoritativeDeclares this server as the authority for its subnet

The authoritative directive is important. Without it, if a client moves from a different network and requests its old IP, ISC DHCP will silently refuse and the client will keep retrying its old (now invalid) address. authoritative tells clients “if I don’t know about that lease, forget it and take a new one.”

Subnet Declaration:

The subnet covers the lab’s private range — 10.99.99.0/24. The dynamic pool runs from .80 to .200. I intentionally start the pool at .80 to leave the lower range for static infrastructure containers and future use. The .1 address is the Proxmox host’s bridge gateway, which handles inter-container routing.

Static Reservations:

Each infrastructure container gets a fixed-address reservation tied to its MAC address. Proxmox assigns MACs in the 00:1a:2b:xx:xx:xx range for LXC containers by default. The mapping is straightforward:

ContainerMACIPRole
dhcp-srv00:1a:2b:a3:ec:58.2DHCP server
mon-srv00:1a:2b:35:9e:14.10Security monitoring (monitoring stack)
web-proxy00:1a:2b:e2:df:11.11Reverse proxy / TLS
media-srv00:1a:2b:55:0c:64.13Media stack
file-srv00:1a:2b:94:00:9c.14File server
dev-srv00:1a:2b:41:af:0a.20Dev workspace
vpn-srv00:1a:2b:53:0c:f5.25VPN endpoint

Note that I reserved the DHCP server’s own MAC-to-IP mapping even though it’s statically configured at the OS level. This is purely for consistency — the lease table won’t show an unknown entry for its own address, and if I ever switch the container to DHCP for some reason, the reservation will kick in automatically.

Starting and Testing the Server

Start the Service

systemctl restart isc-dhcp-server
systemctl status isc-dhcp-server

Check the logs to confirm it’s running:

journalctl -u isc-dhcp-server -n 20 --no-pager

Verifying Lease Assignment

When you create a new Proxmox container with ip=dhcp in its network config, you should see it appear in the leases file:

cat /var/lib/dhcp/dhcpd.leases

A healthy lease looks like:

lease 10.99.99.80 {
  starts 5 2026/05/23 12:34:56;
  ends 5 2026/05/23 12:34:56;
  cltt 5 2026/05/23 12:34:56;
  binding state active;
  next binding state free;
  rewind binding state free;
  hardware ethernet 00:1a:2b:ab:cd:ef;
}

Testing from a Client Container

From any new container that received a DHCP lease, verify:

ip a show eth0
ip route show
cat /etc/resolv.conf

You should see the assigned IP, the gateway (10.99.99.1), and the DNS servers (8.8.8.8, 1.1.1.1).

Pitfalls I Ran Into

1. Silent Failures on Interface Mismatch

If INTERFACESv4 doesn’t match the actual network interface name, dhcpd starts without error but never sees any DISCOVER messages. Double-check ip a output — in LXC containers, the interface is almost always eth0.

2. Multiple DHCP Servers on the Same Broadcast Domain

If your upstream router also has DHCP enabled, you’ll see conflicting leases. Either disable DHCP on the router, or configure a DHCP relay. In my setup, I disabled DHCP on the ISP router and let the lab’s ISC DHCP server handle everything on the 10.99.99.0/24 subnet.

3. Lease Time for Ephemeral Containers

24 hours is fine for infrastructure containers, but if you frequently create and destroy test containers, consider reducing default-lease-time to something like 3600 (1 hour) to avoid lease exhaustion.

4. Container IDs vs IPs

Proxmox assigns container IDs (100, 101, 102…) but these have no relation to IP addresses. Keep a mapping in your documentation — it’s essential as the lab grows.

Resource Usage

ISC DHCP Server is incredibly lightweight. On a Debian 13 LXC:

  • Memory: ~3 MB RSS (peaks at ~11 MB after a client storm)
  • Disk: ~2.5 MB for the binary and config
  • CPU: negligible — typically under 100ms of CPU time over days

There’s no excuse not to run a dedicated DHCP server in your lab.

Alternative: Kea DHCP

ISC DHCP Server is in maintenance mode upstream; the Internet Systems Consortium now recommends Kea as the replacement. Kea offers a modern REST API, database-backed lease storage, and better performance at scale. For a homelab with fewer than 50 clients, ISC DHCP is still perfectly adequate and simpler to configure. Kea will be covered in a future post.

Summary

Running a dedicated ISC DHCP Server in an LXC container gives you:

  • Full control over IP allocations
  • Static reservations for all infrastructure containers
  • A dynamic pool for test/transient workloads
  • Zero dependency on upstream router config
  • A lightweight service that costs basically nothing in resources

The entire configuration is a single file (/etc/dhcp/dhcpd.conf) with fewer than 30 meaningful lines. If a container dies or needs to be rebuilt, the DHCP config is the first thing I restore — everything else gets its IP from the server automatically.

This is part of a series documenting a Proxmox VE homelab infrastructure.