Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion dist/index.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/index.js.map

Large diffs are not rendered by default.

86 changes: 76 additions & 10 deletions src/setup_builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,26 +68,88 @@ export async function getNumCPUs(): Promise<number> {
}
}

/**
* Configures systemd-resolved to listen on all interfaces (not just loopback)
* so that BuildKit build containers on bridge networks can reach the DNS cache.
*
* By default, systemd-resolved only listens on 127.0.0.53, which is not
* reachable from containers in their own network namespace. This adds a
* drop-in config to make it listen on 0.0.0.0:53.
*
* See: https://github.com/moby/buildkit/issues/5009
*/
async function configureSystemdResolvedForBuildkit(): Promise<void> {
try {
await execAsync(`sudo mkdir -p /etc/systemd/resolved.conf.d`);
await execAsync(
`echo '[Resolve]\nDNSStubListenerExtra=0.0.0.0' | sudo tee /etc/systemd/resolved.conf.d/buildkit-dns.conf`,
);
await execAsync(`sudo systemctl restart systemd-resolved`);
core.info(
"Configured systemd-resolved to listen on all interfaces for BuildKit DNS caching",
);
} catch (error) {
core.warning(
`Failed to configure systemd-resolved: ${(error as Error).message}`,
);
}
}

/**
* Gets the host's primary routable IP address, which is reachable from
* BuildKit build containers on any network mode (host, bridge, custom).
*
* Falls back to public DNS servers if the routable IP cannot be determined.
*/
async function getRoutableHostDns(): Promise<string[]> {
// Public DNS fallback in case we can't determine the host's routable IP
const publicDnsFallback = ["8.8.8.8", "8.8.4.4", "1.1.1.1", "1.0.0.1"];

try {
// Get the host's source IP for internet-bound traffic
const { stdout } = await execAsync(
`ip route get 1.1.1.1 | grep -oP 'src \\K[0-9.]+'`,
);
const hostIp = stdout.trim();

if (hostIp && hostIp !== "127.0.0.53") {
core.info(
`Using host routable IP ${hostIp} as sole DNS nameserver for BuildKit (systemd-resolved cache)`,
);
// Only use the host IP (backed by systemd-resolved cache).
// Do NOT include public DNS fallbacks — BuildKit round-robins across
// all nameservers rather than using them as ordered fallbacks, which
// would bypass the cache for ~50% of queries and defeat the purpose.
// systemd-resolved itself already has upstream fallback configured.
return [hostIp];
}
} catch (error) {
core.warning(
`Failed to determine host routable IP: ${(error as Error).message}`,
);
}

core.info("Falling back to public DNS nameservers (no local cache)");
return publicDnsFallback;
}

async function writeBuildkitdTomlFile(
parallelism: number,
addr: string,
dnsNameservers: string[],
): Promise<void> {
const jsonConfig: TOML.JsonMap = {
root: "/var/lib/buildkit",
grpc: {
address: [addr],
},
// Configure explicit DNS nameservers to avoid issues with systemd-resolved stub resolver.
// Point BuildKit at the host's systemd-resolved cache via a routable IP.
// This avoids the known issue where BuildKit falls back to hardcoded public DNS
// (8.8.8.8/8.8.4.4) because it can't use the 127.0.0.53 stub resolver from
// containers in separate network namespaces.
// See: https://github.com/moby/buildkit/issues/5009
dns: {
nameservers: [
"8.8.8.8",
"8.8.4.4",
"1.1.1.1",
"1.0.0.1",
"9.9.9.9",
"149.112.112.112",
],
nameservers: dnsNameservers,
},
registry: {
"docker.io": {
Expand Down Expand Up @@ -133,7 +195,11 @@ export async function startBuildkitd(
driverOpts?: string[],
): Promise<string> {
try {
await writeBuildkitdTomlFile(parallelism, addr);
// Configure systemd-resolved to listen on a routable address so BuildKit
// build containers can use the host's DNS cache from any network namespace.
await configureSystemdResolvedForBuildkit();
const dnsNameservers = await getRoutableHostDns();
await writeBuildkitdTomlFile(parallelism, addr, dnsNameservers);

// Parse driver-opts to extract environment variables
const envVars: Record<string, string> = {};
Expand Down