I got tired of constantly editing my /etc/hosts
file for every new project. I wanted to use clean, convenient local domains like project-one.app.loc
or api.my-app.app.loc
without having to define each one manually.
My solution is Dnsmasq
, a lightweight DNS server that is perfect for this task. It allows me to set up wildcard domains (e.g., *.app.loc
), where any subdomain automatically points to my local machine. It’s a set-it-and-forget-it setup that has saved me countless hours.
In this guide, I’ll walk you through how I set up dnsmasq
on modern Linux distributions like Ubuntu 24.04 and Fedora 42. I’ll also show you how I resolve the common conflict with the default systemd-resolved
service to create a powerful and convenient local development environment.
Step 1: Install Dnsmasq
First things first, I’ll get the package installed from the standard repositories.
|
|
Step 2: Configure Dnsmasq
Now for my favorite part—the configuration. This is where I tell dnsmasq
how to handle my local domains and where to forward all other requests.
I open the configuration file using my editor of choice (nano
is great for this):
|
|
I find it cleanest to scroll to the very end of the file and add my custom settings there. This way, I don’t mix them up with the defaults. Here’s the exact configuration I use:
|
|
Here’s a quick breakdown of what these settings do for me:
listen-address=127.0.0.1
: This forcesdnsmasq
to listen for requests only on my local machine. It’s an important security measure.domain-needed
&bogus-priv
: These preventdnsmasq
from forwarding incomplete or private network requests out to the public internet.address=/app.loc/127.0.0.1
: This is the core of my wildcard setup. Any address ending in.app.loc
resolves to127.0.0.1
.server=...
: This tellsdnsmasq
where to forward all other requests (like forgoogle.com
).
Step 3: Resolve the Conflict with systemd-resolved
On modern Linux systems, there’s a common hurdle: a service called systemd-resolved
is already using DNS port 53, which prevents dnsmasq
from starting. Here’s how I handle this conflict.
I open the systemd-resolved
configuration file:
|
|
Inside the [Resolve]
section, my goal is to free up port 53. I find the DNSStubListener
line, uncomment it (remove the #
), and set its value to no
. I also tell systemd-resolved
to use my dnsmasq
instance as its own DNS server.
|
|
I save the file and exit the editor.
Step 4: Apply Changes and Run the First Check
With the configs in place, it’s time to apply the changes. I restart both services to make sure everything is reloaded correctly.
|
|
Next, I confirm that dnsmasq
has started successfully and claimed port 53.
|
|
In the output, I look for a dnsmasq
process listening on 127.0.0.1:53
.
Then, I make sure dnsmasq
itself is working by sending it a direct query:
|
|
If I see the domain resolving to 127.0.0.1
in the ANSWER SECTION
, I know my dnsmasq
setup is perfect!
Step 5: Configure the System to Use Dnsmasq
Okay, dnsmasq
is running, but my system doesn’t know to use it yet. The final step is to tell my entire OS to use it as the primary DNS server. I do this with NetworkManager
, the standard tool on most modern desktops.
First, I need the name of my active network connection:
|
|
I look for my main connection in the list (it’s usually something like Wired connection 1
or MyWifi
).
Now, I run the following commands, replacing "YOUR_CONNECTION_NAME"
with the name I just found:
|
|
As a final check, I always like to inspect /etc/resolv.conf
by running cat /etc/resolv.conf
. It should now contain just one line: nameserver 127.0.0.1
.
Step 6: The Final Test!
This is my favorite part—the moment of truth. I run two quick tests to make sure everything works.
I test my internet access:
|
|
And then I test my new local domain:
|
|
If both commands succeed, then I’m all set! I now have a powerful and convenient local DNS environment, and I can finally stop editing /etc/hosts
for good.
Happy coding