Skip to content

fix(proxmox): set guest DNS via cloud-init resolv_conf in vendor snippet - #342

Merged
v0l merged 4 commits into
masterfrom
fix/cloudinit-guest-dns
Jul 30, 2026
Merged

fix(proxmox): set guest DNS via cloud-init resolv_conf in vendor snippet#342
v0l merged 4 commits into
masterfrom
fix/cloudinit-guest-dns

Conversation

@v0l

@v0l v0l commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Problem

Customers on the Alpine image (and other minimal images) booted with an empty /etc/resolv.conf and no working DNS.

Root cause

LNVPS assigns IPs statically via Proxmox cloud-init (ipconfig0, no DHCP). When no explicit nameserver is set, Proxmox falls back to the host's /etc/resolv.conf and hands those servers to the guest's cloud-init network config — for every image. The failure is entirely guest-side rendering:

  • Ubuntu/Debian/CentOS render cloud-init DNS through systemd-resolved / resolvconf, so /etc/resolv.conf gets populated (Ubuntu also has systemd-resolved fallback DNS masking the issue).
  • Alpine uses busybox ifupdown with cloud-init's ENI renderer. It writes dns-nameservers into /etc/network/interfaces.d/50-cloud-init, which only reaches /etc/resolv.conf if openresolv/resolvconf is installed — the stock Alpine cloud image doesn't ship it. So the DNS it was handed goes nowhere.

Just setting --nameserver explicitly would not fix this, since the gap is in how Alpine applies whatever DNS it receives.

Fix

Extend the shared cloud-init vendor snippet (ensure_vendor_snippet) to force a fixed set of public resolvers into every guest:

#cloud-config
ssh_deletekeys: false
manage_resolv_conf: true
resolv_conf:
  nameservers:
    - 1.1.1.1
    - 8.8.8.8
    - 9.9.9.9
    - 2606:4700:4700::1111
    - 2001:4860:4860::8888
    - 2620:fe::fe

cloud-init's resolv_conf module writes /etc/resolv.conf directly, bypassing the guest network renderer. Fixes Alpine, NixOS, and any minimal image in one uniform change. The resolver list (Cloudflare, Google, Quad9 — IPv4 + IPv6) is a fixed constant; no host SSH read is performed.

Adds pure helper build_vendor_snippet with unit tests.

Caveats

  1. cloud-init's resolv_conf module runs per-instance (once), so this fixes newly provisioned VMs and reinstalls. Existing affected VMs need a reinstall (or manual cloud-init clean / resolv.conf edit).
  2. systemd-resolved distros (Ubuntu) will have their stub /etc/resolv.conf replaced with a direct file — benign for a VPS.

Tests

  • test_build_vendor_snippet — empty list, an IPv4+IPv6 pair, and the production constant (asserts both IPv4 and IPv6 entries)

All host::proxmox tests pass.

@v0l v0l added bug Something isn't working host:proxmox labels Jul 30, 2026
v0l added 4 commits July 30, 2026 17:24
Statically-addressed VMs (ipconfig0, no DHCP) relied on each image's own
network renderer to apply the DNS servers Proxmox provides. Minimal images
silently dropped them: Alpine's busybox ifupdown writes dns-nameservers to
the interfaces file, which only reaches /etc/resolv.conf when openresolv is
installed (the stock Alpine cloud image lacks it), so guests booted with an
empty /etc/resolv.conf and no working DNS.

Extend the shared cloud-init vendor snippet to mirror the Proxmox host's
/etc/resolv.conf into 'manage_resolv_conf: true' + 'resolv_conf.nameservers',
forcing cloud-init to write /etc/resolv.conf directly regardless of the guest
network renderer. This matches Proxmox's own host-resolv.conf fallback and
fixes Alpine, NixOS and any other minimal image. Falls back to the previous
snippet (no DNS block) when the host has no resolvers.

Adds parse_resolv_conf_nameservers + build_vendor_snippet helpers with tests.
Instead of reading the Proxmox host's /etc/resolv.conf over SSH, force a
fixed set of public resolvers into the cloud-init vendor snippet: Cloudflare,
Google and Quad9, including their IPv6 variants. Drops the
parse_resolv_conf_nameservers helper and the extra SSH round-trip.
…ruct

Replace hand-concatenated YAML with CloudInitVendorData / CloudInitResolvConf
structs serialised via serde_json. JSON is a strict subset of YAML, so the
output (prefixed with the #cloud-config header) is valid cloud-config while
eliminating fragile manual indentation/escaping. No new dependency.
…rde_yml

Swap serde_json for serde_yml so the on-disk lnvps-vendor.yaml is proper
cloud-config YAML instead of JSON-in-a-.yaml-file. Adds serde_yml to the
workspace deps. Test now round-trips the snippet through serde_yml to assert
structure rather than matching an exact string.
@v0l
v0l force-pushed the fix/cloudinit-guest-dns branch from d56218f to e9fc557 Compare July 30, 2026 16:36
@v0l
v0l merged commit d671f0f into master Jul 30, 2026
6 checks passed
@v0l
v0l deleted the fix/cloudinit-guest-dns branch July 30, 2026 16:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working host:proxmox

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant