tourze/dns-server-bundle
A Symfony bundle that integrates a DNS server into your PHP application. It provides configuration and service wiring for running DNS services, with extensible handlers for resolving queries and serving records. Suitable for custom DNS tooling and local development.
composer require tourze/dns-server-bundle:^0.1.0. Pin the constraint — this is a pre-1.0 package and the 0.1.0 release notes are a bare changelog link with no itemized changes, so treat the 0.0.1 → 0.1.0 delta as unknown until you diff it.DnsServerServiceProvider, no config/dns.php to publish, and the bundle's DependencyInjection/Configuration.php tree is read by Symfony's config loader, not Laravel's. Expect to write a thin bridge yourself.README.md — usually the only place with a runnable example for tourze packages.src/ root for the server entrypoint class (commonly DnsServer, Server, or *Kernel) and the resolver/handler interfaces.DependencyInjection/Configuration.php — the authoritative list of config keys and their defaults.Resources/config/services.yaml — shows which services are public, which are tagged, and what the constructor graph looks like.127.0.0.1:5353, and run it from php artisan tinker or a scratch command. Confirm with dig @127.0.0.1 -p 5353 example.com A +short. Do this before wiring anything into a real workflow — it validates that the constructor dependencies resolve outside a Symfony kernel.Wrap the server in an Artisan command, not a controller. Long-running listener in a web request will hit max_execution_time and hold a PHP-FPM worker hostage. A dns:serve command with $this->output->writeln() for lifecycle logging is the natural home.
Register as a container singleton. In a DnsServerServiceProvider:
$this->app->singleton(DnsServer::class, fn ($app) => new DnsServer(
host: config('dns.host', '127.0.0.1'),
port: config('dns.port', 5353),
// ...resolve handler/resolver deps from the container
));
Manually mirror only the config keys you actually use from the bundle's Configuration tree into config/dns.php. Do not try to load the Symfony tree — the key shapes differ (Symfony nests under a bundle alias root, Laravel does not).
Handler/resolver registration via tags. Symfony bundles typically collect resolvers with ->tag('dns.resolver') and a compiler pass. In Laravel, replace that with a tagged binding ($this->app->tag([...], 'dns.resolver')) or an explicit array injected into the server. Check src/ for a compiler pass class — if one exists, that's your list of extension points, and it will silently do nothing under Laravel.
Zone/record data from the app, not the bundle. Keep records in Eloquent or a JSON file and have your resolver read from a repository; the bundle's job is transport and dispatch, not storage. This keeps the resolver testable without a socket.
Testing pattern. Two layers:
setUp, shell out to dig (or send a hand-built packet over stream_socket_client), assert the wire response, then stop the loop. Always stop the loop in tearDown or the suite hangs.Process supervision. Under systemd/Supervisor, or a dedicated dns container in docker-compose exposing both 53/udp and 53/tcp. Health check with dig @127.0.0.1 -p 5353 health.internal A +time=1 +tries=1.
Forwarding setups. If you're placing this in front of an existing resolver, chain upstream to 127.0.0.53 (systemd-resolved) or your VPC resolver and cache aggressively — a naive forwarder per query will be slower than the thing it replaced.
git diff 0.0.1..0.1.0 -- src/ and check constructor signatures and interface methods. Pin exactly (0.1.0, not ^0.1) until you've read the diff.config() returns arrays with different nesting, and %env()%-style placeholders in the bundle's YAML are resolved by Symfony's container, not yours. Any env var referenced in services.yaml must be bound manually.setcap cap_net_bind_service=+ep on the PHP binary or run on 5353 and redirect. Don't casually run the container as root.dig @127.0.0.1 -p 5353 +ignore big.example.com TXT and compare to a run without +ignore. If large answers come back empty or truncated with no TC bit, clients will hang on retries.dig +noall +answer @127.0.0.1 -p 5353 name A for correctness; dig +qr to see the query; tcpdump -i lo -n udp port 5353 -X when you suspect malformed packets the client silently drops; strace -f -e trace=network -p <pid> to confirm the socket is actually bound where you think. A silent failure with no response is nearly always a bind failure or a firewall, not a resolver bug — check ss -lunp | grep 5353 first.src/ — resolver/handler/middleware contracts. Bundles in this family tend to ship an abstract base class plus a chain; implement the interface rather than subclassing, since 0.x minor releases freely reshuffle abstract internals but rarely the interfaces.dnsdist/unbound rather than patching the loop.How can I help you explore Laravel packages today?