<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Adam Migus</title>
    <description>The latest articles on DEV Community by Adam Migus (@amigus).</description>
    <link>https://dev.to/amigus</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1961172%2F53d45246-6df2-439b-90ef-29144c1a5b2a.png</url>
      <title>DEV Community: Adam Migus</title>
      <link>https://dev.to/amigus</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/amigus"/>
    <language>en</language>
    <item>
      <title>Automating Dnsmasq with Ansible</title>
      <dc:creator>Adam Migus</dc:creator>
      <pubDate>Mon, 06 Oct 2025 16:27:38 +0000</pubDate>
      <link>https://dev.to/amigus/automating-dnsmasq-with-ansible-o9c</link>
      <guid>https://dev.to/amigus/automating-dnsmasq-with-ansible-o9c</guid>
      <description>&lt;p&gt;Dnsmasq is a DHCP server and DNS forwarder designed for small networks. It's not built for scale but highly extendable and flexible. I used that extensibility to add some automation and manageability:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/amigus/dnsmasq-web" rel="noopener noreferrer"&gt;dnsmasq-web&lt;/a&gt;: a REST (JSON/HTTP) API that provides access to client, lease, and request data and reservation management.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://amigus.github.io/dnsmasq-ansible/" rel="noopener noreferrer"&gt;amigus.dnsmasq&lt;/a&gt;: a collection of Ansible Roles available on Ansible &lt;a href="https://galaxy.ansible.com/ui/repo/published/amigus/dnsmasq/" rel="noopener noreferrer"&gt;Galaxy&lt;/a&gt; that install and configure Dnsmasq as a DHCP and/or DNS server&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;My main goal with the Ansible collection was to replace the terse configuration syntax of Dnsmasq with more readable YAML, e.g.,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;dnsmasq_dns_servers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;192.168.1.253&lt;/span&gt;
    &lt;span class="na"&gt;domain&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;wired.lan&lt;/span&gt;
    &lt;span class="na"&gt;network&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;192.168.2.0/24&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;192.168.1.1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Translates to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="py"&gt;rev-server&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;192.168.2.0/24,192.168.1.253&lt;/span&gt;
&lt;span class="py"&gt;server&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;192.168.1.1&lt;/span&gt;
&lt;span class="py"&gt;server&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/wired.lan/192.168.1.253&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A DHCP server that leases the entire subnet starting from &lt;code&gt;.10&lt;/code&gt; using &lt;code&gt;.1&lt;/code&gt; as the gateway:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;dnsmasq_dhcp_interfaces&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;device&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;eth0&lt;/span&gt;
    &lt;span class="na"&gt;router&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
    &lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The YAML does not need to contain the IP subnet information because Ansible gets it from the interface.&lt;/p&gt;

&lt;p&gt;For example, if the &lt;code&gt;eth0&lt;/code&gt; interface of the server is &lt;code&gt;192.168.100.2/24&lt;/code&gt;, the resulting configuration parameters are:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="py"&gt;dhcp-range&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;interface:eth0,192.168.100.10,192.168.100.254&lt;/span&gt;
&lt;span class="py"&gt;dhcp-option&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;interface:eth0,option:router,192.168.100.1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  SQLite Database
&lt;/h3&gt;

&lt;p&gt;Dnsmasq does not use a database; it keeps &lt;em&gt;current&lt;/em&gt; lease information in an (undocumented) flat file and does not have an API. However, lease management can be delegated via the &lt;code&gt;dhcp-script&lt;/code&gt; configuration parameter.&lt;/p&gt;

&lt;p&gt;The role collection contains an SQLite database schema and a shell script for implementing a lease management database. It has &lt;em&gt;requests&lt;/em&gt;, &lt;em&gt;leases&lt;/em&gt;, and &lt;em&gt;clients&lt;/em&gt; tables.&lt;/p&gt;

&lt;h3&gt;
  
  
  REST API
&lt;/h3&gt;

&lt;p&gt;Dnsmasq also supports reading DHCP lease reservations from files in a specific directory. The role collection installs dnsmasq-web, which uses that feature to enable management of DHCP lease reservations over HTTP and/or exposes the DHCP database data.&lt;/p&gt;

&lt;p&gt;The database and REST API are optional and only present on the target system when the required options are in the configuration YAML.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo: run a server on localhost
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;p&gt;Ansible needs &lt;a href="https://www.python.org/" rel="noopener noreferrer"&gt;Python&lt;/a&gt;, and the role collection needs the Python &lt;a href="https://pypi.org/project/netaddr/" rel="noopener noreferrer"&gt;netaddr&lt;/a&gt; library. The system must also be an Alpine (apk), Red Hat (dnf), or OpenSUSE (zypper) variant.&lt;/p&gt;

&lt;p&gt;The variables below will configure DHCP to offer &lt;code&gt;.1&lt;/code&gt; as the network gateway and lease the whole IP subnet starting from &lt;code&gt;.10&lt;/code&gt;. It will also set itself as the DNS server for all clients and forward all queries to &lt;code&gt;1.1.1.1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To configure the DNS only, omit the &lt;code&gt;dnsmasq_dhcp_interfaces&lt;/code&gt; variable. To offer the DHCP server as the gateway, omit the &lt;code&gt;router&lt;/code&gt; attribute.&lt;/p&gt;

&lt;h3&gt;
  
  
  Target localhost
&lt;/h3&gt;

&lt;p&gt;Create an &lt;a href="https://docs.ansible.com/ansible-core/2.19/inventory_guide/" rel="noopener noreferrer"&gt;Inventory&lt;/a&gt;, add the &lt;code&gt;localhost&lt;/code&gt; to the &lt;code&gt;dnsmasq&lt;/code&gt; group, add the required variables to &lt;code&gt;vars&lt;/code&gt;, and save it as &lt;code&gt;inventory.yaml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;dnsmasq&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;localhost&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;ansible_connection&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;local&lt;/span&gt;
  &lt;span class="na"&gt;vars&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;dnsmasq_dhcp_interfaces&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[{&lt;/span&gt; &lt;span class="nv"&gt;device&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;eth0&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;router&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;1&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;start&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;10&lt;/span&gt; &lt;span class="pi"&gt;}]&lt;/span&gt;
    &lt;span class="na"&gt;dnsmasq_dns_options&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;bogus-priv&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;domain-needed&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;no-resolv&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;dnsmasq_dns_servers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[{&lt;/span&gt; &lt;span class="nv"&gt;address&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;1.1.1.1&lt;/span&gt; &lt;span class="pi"&gt;}]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Run the playbook
&lt;/h3&gt;

&lt;p&gt;Then install Ansible and the amigus.dnsmasq collection, and run the Playbook:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;ansible-core
ansible-galaxy collection &lt;span class="nb"&gt;install &lt;/span&gt;amigus.dnsmasq
ansible-playbook &lt;span class="nt"&gt;-i&lt;/span&gt; inventory.yaml amigus.dnsmasq.dnsmasq
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  More information
&lt;/h2&gt;

&lt;p&gt;For more information, like how to configure a static (reservation-only) server, check out the &lt;a href="https://amigus.github.io/dnsmasq-ansible" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>ansible</category>
      <category>dhcp</category>
      <category>dns</category>
      <category>dnsmasq</category>
    </item>
  </channel>
</rss>
