nic
A minimal, standalone network configuration tool for Linux. Single binary, zero dependencies, plain-text config.
Replaces systemd-networkd, netplan, and /etc/network/interfaces with one config file and one command.
Features
- Declarative config at
/etc/nic.conf
- Rollback-safe iproute2 commands (including abbreviated syntax like
ip l s eth0 up)
- Shortcut syntax for common operations
- Interface aliasing and MAC pinning
- Built-in DHCP client with fallback (dhclient, dhcpcd, udhcpc)
- WiFi support (wpa_supplicant, iwd)
- DNS management with
/etc/resolv.conf guarding
- IPv4 and IPv6 firewall rules via
iptables-restore
- Config includes with natural sort order
- Dry-run mode
- Automatic rollback with confirmation timeout
- Init system support: systemd, OpenRC, SysV, runit
- IPv6 with DAD awareness
- No external Go dependencies
Install
make build
sudo make install # copies binary + default config
sudo make install-systemd # also installs systemd service
sudo make install-openrc # OpenRC
sudo make install-sysv # SysV init
sudo make install-runit # runit
The systemd installer does not stop or mask the existing network stack. After
validating /etc/nic.conf, either disable conflicting services yourself or run
sudo NIC_DISABLE_SYSTEM_SERVICES=1 make install-systemd.
When that opt-in mode masks systemd-resolved, it replaces only its runtime
/etc/resolv.conf symlink with a regular resolver file; administrator-managed
resolver files and other symlink targets are left unchanged.
For staged/package installs, all targets honor DESTDIR; PREFIX,
SYSCONFDIR, and init-directory variables can also be overridden.
Usage
nic - network interface configurator
Usage: nic <command> [options]
Commands:
start Apply network configuration
stop Tear down network configuration
restart [options] Stop and re-apply configuration
reload [options] Re-apply configuration (shows diff)
status Show current network state
show Show parsed configuration
dry-run Show what would be done without applying
confirm Confirm changes after reload/restart with timeout
version Show version
Options:
--config=PATH Config file path (default: /etc/nic.conf)
--verbose, -v Show commands being executed
--confirm-timeout=N Revert after N seconds if not confirmed (default: 10)
--no-rollback Apply restart/reload without a confirmation watcher
--daemon, -d Run in daemon mode (keeps DHCP clients running)
--force Skip confirmation prompts
--help, -h Show this help
--version, -V Show version
Configuration
Interface control
# Full iproute2
ip link set eth0 up
# Abbreviated (handled by ip binary)
ip l s eth0 up
# Shortcuts
if eth0 up
if eth0 down
up eth0
down eth0
IP addresses
# Full iproute2
ip address add 192.168.1.100/24 dev eth0
# Abbreviated
ip a a 192.168.1.100/24 dev eth0
# Shortcut (auto-adds /32 or /128 if no prefix)
ip 192.168.1.100/24 eth0
ip fd76:1e4b:375a::/48 eth0
Routes
# Full iproute2
ip route add default via 192.168.1.1 dev eth0
# Abbreviated
ip r a default via 192.168.1.1 dev eth0
# Shortcut
route default via 192.168.1.1 eth0
route 10.0.0.0/8 via 192.168.1.1 eth0
route 172.16.0.0/12 eth0
DNS
nameserver 1.1.1.1
ns 8.8.8.8
Writes /etc/resolv.conf and protects it with chattr +i. Static
nameserver entries take precedence over DHCP-provided entries; without
static entries, DNS from active DHCP sessions is merged deterministically.
DHCP
dhcp eth0 # auto-detects client
dhcp eth0 dhclient # force specific client
dhcpv6 eth0 # best-effort DHCPv6
dhcpv6 eth0 required # abort the whole configuration if DHCPv6 fails
dhcp4 and dhcpv4 are accepted as aliases for dhcp, and dhcp6 for
dhcpv6.
Supports dhclient, dhcpcd, and udhcpc.
The native client is tried first. If it cannot obtain a lease, nic falls back
to the first installed external client. Specify native, dhclient,
dhcpcd, or udhcpc to force one implementation.
dhcpv6 is best-effort: if no lease is obtained but the interface is
configured by another address family (a DHCPv4 lease or a static address), nic
logs a warning and continues. The configuration fails only when the interface
is left with no address at all. Add required to treat any DHCPv6 failure as
fatal, which is the equivalent of NetworkManager's ipv6.may-fail=no.
WiFi
wifi MyNetwork MyPassword # auto-detect interface
wifi MyNetwork MyPassword wlan0 # specify interface
Uses wpa_supplicant or iwd, with WPA3 (SAE) support.
Because the configuration contains the WiFi passphrase, every file containing
a wifi directive must be owned appropriately and have mode 0600. Passwords
are never placed in command-line arguments and are stored only in protected
runtime backend configuration.
Firewall
iptables /etc/nic.rules.v4
ip6tables /etc/nic.rules.v6
Each directive points to a complete rules file in iptables-save/
iptables-restore format. iptables loads IPv4 rules with
iptables-restore; ip6tables loads IPv6 rules with
ip6tables-restore. Relative paths are resolved from the configuration file
that contains the directive.
A rules file is the complete desired state for its address family, not a
partial table update. If a table exists in the live rules but is omitted from
the file, nic flushes it, removes its user-defined chains, and leaves its
built-in chains with ACCEPT policies. This also clears rules and user chains
from tables introduced by a failed configuration when rollback restores a
baseline that did not contain them.
The default installation includes conservative example files at
/etc/nic.rules.v4 and /etc/nic.rules.v6, but both directives are commented
out in /etc/nic.conf. Review the rules—especially the allowed SSH port—before
enabling either family.
Rules are loaded at the point the directive appears, so place it before the
interfaces it is meant to protect are brought up. Note that the two address
families filter DHCP differently: the DHCPv4 client uses a packet socket and is
not seen by the INPUT chain, while the DHCPv6 client uses a UDP socket, so a
default-drop IPv6 policy must accept UDP port 546 as the shipped example does.
To remain compatible with iptables releases that predate
iptables-restore --wait, nic invokes the restore command without that option
and retries xtables-lock contention for up to 10 seconds.
Firewall state participates in NIC's baseline and confirmation-timeout
rollback. Only a family named by the current or desired configuration is
captured and restored; configuring IPv4 rules does not alter IPv6 rules, or
vice versa. During a reload, a family replaced by the desired configuration is
kept on its current policy until its new rules directive runs; nic does not
briefly restore the pre-nic firewall first.
Aliases and MAC pinning
# Static alias
alias my_eth enp14s0
# Pin by MAC address (resolved at runtime)
pin my_eth aa:bb:cc:dd:ee:ff
# Use alias in subsequent commands
if my_eth up
ip 192.168.1.100/24 my_eth
Includes
include nic.d/*.conf
Paths are relative to the config file directory. Files are loaded in natural sort order (2.conf before 10.conf).
# Full line comment
ip 10.0.0.1/24 eth0 # inline comment
Example: LACP bond with VLAN
# /etc/nic.conf
# Create bond
ip link add bond0 type bond mode 802.3ad xmit_hash_policy layer2+3 lacp_rate fast
ip link set eth0 down
ip link set eth1 down
ip link set eth0 master bond0
ip link set eth1 master bond0
if bond0 up
# VLAN on top of bond
ip link add link bond0 name bond0.100 type vlan id 100
ip 192.168.100.10/24 bond0.100
if bond0.100 up
# Default route and DNS
route default via 192.168.100.1 bond0.100
ns 1.1.1.1
Restart safety
nic restart and nic reload save the current network state before applying changes. If you don't run nic confirm within the timeout (default 10 seconds), the previous state is automatically restored. This prevents locking yourself out over SSH.
reload compares the last applied configuration with the desired one, restores
the pre-nic baseline, and then applies the desired state. Removed addresses,
routes, links, DNS entries, DHCP sessions, and WiFi connections therefore do
not linger. Use --no-rollback only in automation that intentionally accepts
the new configuration immediately.
To keep that rollback guarantee, passthrough accepts link creation and the
restorable link settings (up/down, mtu, MAC address, and master), plus
address, route, and rule add/replace commands. Destructive operations such
as ip link delete, interface renames, namespace moves, and unsupported
iproute2 objects are rejected instead of being applied without a safe inverse.
sudo nic restart --confirm-timeout=30
# test connectivity...
sudo nic confirm
Build
Requires Go 1.22+.
make build # produces ./nic for the host architecture
make test # run tests
make lint # run golangci-lint
Cross-compilation needs no toolchain beyond Go; the binary is pure Go and is
built with CGO_ENABLED=0.
make cross # all targets below, into ./dist
make cross-x86_64 # dist/nic-<version>-x86_64 (GOARCH=amd64)
make cross-arm64 # dist/nic-<version>-arm64
make cross-armv7 # dist/nic-<version>-armv7 (GOARM=7)
make cross-armv6 # dist/nic-<version>-armv6 (GOARM=6)
make cross-riscv64 # dist/nic-<version>-riscv64
Override CROSS_TARGETS to build a subset, and DISTDIR to change the output
directory.
License
nic - network interfaces configurator
Copyright (C) 2026 c0m4r <https://github.com/c0m4r>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Linux® is the registered trademark of Linus Torvalds in the U.S. and other countries.