txeh

package module
v1.8.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 8, 2026 License: Apache-2.0 Imports: 10 Imported by: 67

README

txeh - /etc/hosts mangement

Etc Hosts Management Utility & Go Library

CI codecov Go Report Card Go Reference OpenSSF Scorecard License

Documentation | Go Reference | Releases

/etc/hosts Management

It is easy to open your /etc/hosts file in text editor and add or remove entries. However, if you make heavy use of /etc/hosts for software development or DevOps purposes, it can sometimes be difficult to automate and validate large numbers of host entries.

txeh was initially built as a golang library to support kubefwd, a Kubernetes port-forwarding utility utilizing /etc/hosts heavily, to associate custom hostnames with multiple local loopback IP addresses and remove these entries when it terminates.

A computer's /etc/hosts file is a powerful utility for developers and system administrators to create localized, custom DNS entries. This small go library and utility were developed to encapsulate the complexity of working with /etc/hosts directly by providing a simple interface for adding and removing entries in a /etc/hosts file.

Features:

  • Thread-safe operations (mutex-protected for concurrent use)
  • IPv4 and IPv6 support
  • CIDR range operations for bulk add/remove
  • Preserves comments and file formatting
  • Cross-platform (Linux, macOS, Windows)

txeh Utility

Install

MacOS homebrew users can brew install txn2/tap/txeh, otherwise see releases for packages and binaries for a number of distros and architectures including Windows, Linux and Arm based systems.

Install with go install

When installing with Go please use the latest stable Go release. At least go1.24 or greater is required.

To install use: go install github.com/txn2/txeh/txeh@master

If you are building from a local source clone, use go install ./txeh from the top-level directory of the clone.

go install will typically put the txeh binary inside the bin directory under go env GOPATH, see Go’s Compile and install packages and dependencies for more on this. You may need to add that directory to your $PATH if you encounter the error txeh: command not found after installation, you can find a guide for adding a directory to your PATH at https://gist.github.com/nex3/c395b2f8fd4b02068be37c961301caa7#file-path-md.

Compile and run from source

dependencies are vendored:

go run ./txeh/txeh.go
Use

The txeh CLI application allows command line or scripted access to /etc/hosts file modification.

Example CLI Usage:

 _            _
| |___  _____| |__
| __\ \/ / _ \ '_ \
| |_ >  <  __/ | | |
 \__/_/\_\___|_| |_|

Add, remove and re-associate hostname entries in your /etc/hosts file.
Read more including usage as a Go library at https://github.com/txn2/txeh

Usage:
  txeh [flags]
  txeh [command]

Available Commands:
  add         Add hostnames to /etc/hosts
  completion  Generate the autocompletion script for the specified shell
  help        Help about any command
  list        List hostnames or IP addresses
  remove      Remove a hostname or ip address
  show        Show hostnames in /etc/hosts
  version     Print the version number of txeh


Flags:
  -d, --dryrun                   dry run, output to stdout (ignores quiet)
  -h, --help                     help for txeh
  -m, --max-hosts-per-line int   Max hostnames per line (0=auto, -1=unlimited, >0=explicit)
  -q, --quiet                    no output
  -r, --read string              (override) Path to read /etc/hosts file.
  -w, --write string             (override) Path to write /etc/hosts file.
# point the hostnames "test" and "test.two" to the local loopback
sudo txeh add 127.0.0.1 test test.two

# remove the hostname "test"
sudo txeh remove host test

# remove multiple hostnames
sudo txeh remove host test test2 test.two

# remove an IP address and all the hosts that point to it
sudo txeh remove ip 93.184.216.34

# remove multiple IP addresses
sudo txeh remove ip 93.184.216.34 127.1.27.1

# remove CIDR ranges
sudo txeh remove cidr 93.184.216.0/24 127.1.27.0/28

# quiet mode will suppress output
sudo txeh remove ip 93.184.216.34 -q

# dry run will print a rendered /etc/hosts with your changes without
# saving it.
sudo txeh remove ip 93.184.216.34 -d

# use quiet mode and dry-run to direct the rendered /etc/hosts file
# to another file
sudo txeh add 127.1.27.100 dev.example.com -q -d > hosts.test

# specify an alternate /etc/hosts file to read. writing will
# default to the specified read path.
txeh add 127.1.27.100 dev2.example.com -q -r ./hosts.test

# specify a separate read and write path
txeh add 127.1.27.100 dev3.example.com -r ./hosts.test -w ./hosts.test2

Comments

txeh supports inline comments on host entries, useful for tracking which tool or purpose added specific entries (e.g., docker, development environments).

How Comments Work

Comments use the standard hosts file format: IP HOSTNAME [HOSTNAME...] # comment

Key behavior:

  • Comments are used for grouping: hosts with the same IP AND same comment are placed on the same line
  • Hosts added without a comment only match lines without a comment
  • Hosts added with a comment only match lines with the same comment
  • Comments are never modified on existing lines
  • If no matching line exists, a new line is created
CLI Usage
# Add host with a comment
sudo txeh add 127.0.0.1 myapp --comment "local development"
# Result: 127.0.0.1        myapp # local development

# Add another host with the same comment (groups together)
sudo txeh add 127.0.0.1 myapp2 --comment "local development"
# Result: 127.0.0.1        myapp myapp2 # local development

# Add host with a different comment (new line)
sudo txeh add 127.0.0.1 api --comment "staging environment"
# Result: 127.0.0.1        api # staging environment

# Add host without a comment (only matches lines without comments)
sudo txeh add 127.0.0.1 test
# Result: goes to first 127.0.0.1 line that has NO comment, or creates new line
Library Usage
// Add hosts with a comment
hosts.AddHostWithComment("127.0.0.1", "myapp", "local development")
hosts.AddHostsWithComment("127.0.0.1", []string{"svc1", "svc2"}, "my project services")

// Add hosts without a comment (original behavior)
hosts.AddHost("127.0.0.1", "myhost")
hosts.AddHosts("127.0.0.1", []string{"a", "b", "c"})
Example Scenario

Starting hosts file:

127.0.0.1        localhost
127.0.0.1        app1 app2 # dev services
Command Result
txeh add 127.0.0.1 app3 -c "dev services" app3 added to "dev services" line
txeh add 127.0.0.1 api -c "staging env" New line: 127.0.0.1 api # staging env
txeh add 127.0.0.1 test test added to localhost line (no comment)
Listing and Removing by Comment

List all hosts with a specific comment:

sudo txeh list bycomment "dev services"

Remove all entries with a specific comment (removes entire lines):

sudo txeh remove bycomment "dev services"
Modifying Comments

Comments are never modified on existing lines. To change a comment, remove the hosts first and re-add them with the new comment:

# Remove all entries with the old comment
sudo txeh remove bycomment "old comment"

# Re-add with the new comment
sudo txeh add 127.0.0.1 app1 app2 --comment "new comment"

txeh Go Library

Dependency:

go get github.com/txn2/txeh

Example Golang Implementation:

package main

import (
    "fmt"

    "github.com/txn2/txeh"
)

func main() {
    // Load the system hosts file
    hosts, err := txeh.NewHostsDefault()
    if err != nil {
        panic(err)
    }

    // Add hosts (without comments)
    hosts.AddHost("127.100.100.100", "myapp")
    hosts.AddHost("127.100.100.101", "database")
    hosts.AddHosts("127.100.100.102", []string{"cache", "queue", "search"})

    // Add hosts with comments (for tracking/organization)
    hosts.AddHostWithComment("127.100.100.200", "api.local", "development services")
    hosts.AddHostsWithComment("127.100.100.201", []string{"web.local", "admin.local"}, "frontend apps")

    // Query existing entries
    addresses := hosts.ListAddressesByHost("myapp", true)
    fmt.Printf("myapp resolves to: %v\n", addresses)

    hostnames := hosts.ListHostsByIP("127.100.100.102")
    fmt.Printf("Hosts at 127.100.100.102: %v\n", hostnames)

    // List all hosts with a specific comment
    devHosts := hosts.ListHostsByComment("development services")
    fmt.Printf("Dev service hosts: %v\n", devHosts)

    // Remove entries
    hosts.RemoveHost("database")
    hosts.RemoveHosts([]string{"cache", "queue"})
    hosts.RemoveAddress("127.1.27.1")
    hosts.RemoveAddresses([]string{"127.1.27.15", "127.1.27.14"})

    // Remove all entries with specific comments
    hosts.RemoveByComment("frontend apps")
    hosts.RemoveByComments([]string{"old environment", "deprecated"})

    // RemoveCIDRs returns an error (CIDR parsing can fail)
    if err := hosts.RemoveCIDRs([]string{"10.0.0.0/8"}); err != nil {
        panic(err)
    }

    // Preview changes
    fmt.Println(hosts.RenderHostsFile())

    // Save changes
    err = hosts.Save()
    if err != nil {
        panic(err)
    }
    // Or save to a specific file: hosts.SaveAs("./custom.hosts")
}

Build Release

Build test release:

goreleaser --skip-publish --clean --skip-validate

Build and release:

GITHUB_TOKEN=$GITHUB_TOKEN goreleaser --clean
License

Apache License 2.0


Open source by Craig Johnston, sponsored by Deasil Works, Inc.

Documentation

Overview

Package txeh provides /etc/hosts file management capabilities. It offers a simple interface for adding, removing, and querying hostname-to-IP mappings with thread-safe operations and cross-platform support (Linux, macOS, Windows).

Index

Constants

View Source
const (
	UNKNOWN = 0  // Unknown line type.
	EMPTY   = 10 // Empty line.
	COMMENT = 20 // Comment line starting with #.
	ADDRESS = 30 // Address line with IP and hostnames.
)

Line type constants for HostFileLine.

View Source
const DefaultMaxHostsPerLineWindows = 9

DefaultMaxHostsPerLineWindows is the default maximum number of hostnames per line on Windows. Windows has a limitation where lines with more than ~9 hostnames may not resolve correctly.

Variables

View Source
var ErrNoResolver = errors.New(
	"systemd-resolved not detected (tried resolvectl, systemd-resolve). " +
		"If your system does not cache DNS locally, hosts file changes take effect immediately without flushing",
)

ErrNoResolver is returned when no supported DNS resolver is found on the system. If your system does not cache DNS locally (e.g., no systemd-resolved, dnsmasq, or unbound), hosts file changes take effect immediately without flushing.

Functions

func ExecCommandFunc added in v1.8.0

func ExecCommandFunc() func(string, ...string) *exec.Cmd

ExecCommandFunc returns the current exec command function. This is intended for test code that needs to save and restore the original.

func FlushDNSCache added in v1.8.0

func FlushDNSCache() error

FlushDNSCache flushes the operating system's DNS cache.

Platform commands (all are OS-provided utilities, not installable dependencies):

  • macOS: dscacheutil -flushcache + killall -HUP mDNSResponder (ships with macOS)
  • Linux: resolvectl flush-caches (systemd 239+) or systemd-resolve --flush-caches (older systemd). Returns ErrNoResolver if neither binary is found, which typically means the system does not cache DNS locally and hosts file changes take effect immediately.
  • Windows: ipconfig /flushdns (ships with Windows)

Returns a *FlushError on failure, or nil on unsupported platforms where no resolver is detected.

func SetExecCommandFunc added in v1.8.0

func SetExecCommandFunc(fn func(string, ...string) *exec.Cmd)

SetExecCommandFunc replaces the exec command function. This is intended for test code that needs to mock command execution.

Types

type AddressLocations

type AddressLocations map[string]int

AddressLocations maps an address to its location in the HFL.

type FlushError added in v1.8.0

type FlushError struct {
	Platform string
	Command  string
	Err      error
}

FlushError represents a DNS cache flush failure with platform context.

func (*FlushError) Error added in v1.8.0

func (e *FlushError) Error() string

Error returns a human-readable error message.

func (*FlushError) Unwrap added in v1.8.0

func (e *FlushError) Unwrap() error

Unwrap returns the underlying error.

type HostFileLine

type HostFileLine struct {
	OriginalLineNum int
	LineType        int
	Address         string
	Parts           []string
	Hostnames       []string
	Raw             string
	Trimmed         string
	Comment         string
}

HostFileLine represents a single line in a hosts file.

func ParseHosts

func ParseHosts(path string) ([]HostFileLine, error)

ParseHosts reads and parses a hosts file from the given path.

func ParseHostsFromString added in v1.5.3

func ParseHostsFromString(input string) ([]HostFileLine, error)

ParseHostsFromString parses hosts file content from a string.

type HostFileLines

type HostFileLines []HostFileLine

HostFileLines is a slice of HostFileLine entries.

type HostLocations

type HostLocations map[string]int

HostLocations maps a hostname to an original line number.

type Hosts

type Hosts struct {
	*HostsConfig
	// contains filtered or unexported fields
}

Hosts represents a parsed hosts file with thread-safe operations.

func NewHosts

func NewHosts(hc *HostsConfig) (*Hosts, error)

NewHosts returns a new hosts object.

func NewHostsDefault

func NewHostsDefault() (*Hosts, error)

NewHostsDefault returns a hosts object with default configuration.

func (*Hosts) AddHost

func (h *Hosts) AddHost(addressRaw, hostRaw string)

AddHost adds a host to an address and removes the host from any existing address it may be associated with.

func (*Hosts) AddHostWithComment added in v1.7.0

func (h *Hosts) AddHostWithComment(addressRaw, hostRaw, comment string)

AddHostWithComment adds a host to an address with an inline comment. The comment will appear after the hostnames on the line (e.g., "127.0.0.1 host # comment"). If the address already exists with the same comment, the host is appended to that line (respecting MaxHostsPerLine). If the address exists with a different comment, a new line is created with the specified comment.

func (*Hosts) AddHosts

func (h *Hosts) AddHosts(address string, hosts []string)

AddHosts adds an array of hosts to the first matching address it finds or creates the address and adds the hosts.

func (*Hosts) AddHostsWithComment added in v1.7.0

func (h *Hosts) AddHostsWithComment(address string, hosts []string, comment string)

AddHostsWithComment adds an array of hosts to an address with a comment. All hosts will share the same comment. If the address already exists with the same comment, hosts are appended to that line (respecting MaxHostsPerLine). If the address exists with a different comment, a new line is created.

func (*Hosts) GetHostFileLines

func (h *Hosts) GetHostFileLines() HostFileLines

GetHostFileLines returns a copy of all parsed host file lines.

func (*Hosts) HostAddressLookup

func (h *Hosts) HostAddressLookup(host string, ipFamily IPFamily) (found bool, address string, idx int)

HostAddressLookup returns true if the host is found, the address string, and the index of the host file line. This is part of the public API for consumers that need direct address lookups by IP family.

func (*Hosts) ListAddressesByHost added in v1.5.2

func (h *Hosts) ListAddressesByHost(hostname string, exact bool) [][]string

ListAddressesByHost returns a list of IPs associated with a given hostname.

func (*Hosts) ListHostsByCIDR added in v1.5.2

func (h *Hosts) ListHostsByCIDR(cidr string) [][]string

ListHostsByCIDR returns a list of IPs and hostnames associated with a given CIDR.

func (*Hosts) ListHostsByComment added in v1.7.0

func (h *Hosts) ListHostsByComment(comment string) []string

ListHostsByComment returns all hostnames on lines with the given comment.

func (*Hosts) ListHostsByIP added in v1.5.2

func (h *Hosts) ListHostsByIP(address string) []string

ListHostsByIP returns a list of hostnames associated with a given IP address.

func (*Hosts) Reload

func (h *Hosts) Reload() error

Reload re-reads the hosts file from disk and replaces the in-memory state. This is part of the public API for consumers who manage long-lived Hosts instances.

func (*Hosts) RemoveAddress

func (h *Hosts) RemoveAddress(address string)

RemoveAddress removes all entries (lines) with the provided address.

func (*Hosts) RemoveAddresses

func (h *Hosts) RemoveAddresses(addresses []string)

RemoveAddresses removes all entries (lines) with the provided address.

func (*Hosts) RemoveByComment added in v1.7.0

func (h *Hosts) RemoveByComment(comment string)

RemoveByComment removes all host entries that have the specified comment. This removes entire lines where the comment matches.

func (*Hosts) RemoveByComments added in v1.7.0

func (h *Hosts) RemoveByComments(comments []string)

RemoveByComments removes all host entries that have any of the specified comments. This removes entire lines where the comment matches. This is part of the public API for bulk comment-based cleanup (e.g., kubefwd teardown).

func (*Hosts) RemoveCIDRs added in v1.5.4

func (h *Hosts) RemoveCIDRs(cidrs []string) error

RemoveCIDRs Remove CIDR Range (Classless inter-domain routing) examples:

127.1.0.0/16  = 127.1.0.0  -> 127.1.255.255
127.1.27.0/24 = 127.1.27.0 -> 127.1.27.255

func (*Hosts) RemoveFirstAddress

func (h *Hosts) RemoveFirstAddress(address string) bool

RemoveFirstAddress removes the first entry (line) found with the provided address.

func (*Hosts) RemoveFirstHost

func (h *Hosts) RemoveFirstHost(host string) bool

RemoveFirstHost removes the first hostname entry found and returns true if successful.

func (*Hosts) RemoveHost

func (h *Hosts) RemoveHost(host string)

RemoveHost removes all hostname entries of provided host.

func (*Hosts) RemoveHosts

func (h *Hosts) RemoveHosts(hosts []string)

RemoveHosts removes all hostname entries of the provided host slice.

func (*Hosts) RenderHostsFile

func (h *Hosts) RenderHostsFile() string

RenderHostsFile returns the hosts file content as a formatted string.

func (*Hosts) Save

func (h *Hosts) Save() error

Save renders and writes the hosts file to the configured write path.

func (*Hosts) SaveAs

func (h *Hosts) SaveAs(fileName string) error

SaveAs saves rendered hosts file to the filename specified.

type HostsConfig

type HostsConfig struct {
	ReadFilePath  string
	WriteFilePath string
	// RawText for input. If RawText is set ReadFilePath, WriteFilePath are ignored. Use RenderHostsFile rather
	// than save to get the results.
	RawText *string
	// MaxHostsPerLine limits the number of hostnames per line when adding hosts.
	// This is useful for Windows which has a limitation of ~9 hostnames per line.
	// Values:
	//   0  = auto-detect (Windows: 9, others: unlimited)
	//  -1  = force unlimited (no limit)
	//  >0  = explicit limit
	MaxHostsPerLine int
	// AutoFlush triggers a DNS cache flush after every successful Save/SaveAs.
	// Flush failures are returned as *FlushError, distinguishable via errors.As.
	AutoFlush bool
}

HostsConfig contains configuration for reading and writing hosts files.

type IPFamily added in v1.4.0

type IPFamily int64

IPFamily represents the IP address family (IPv4 or IPv6).

const (
	IPFamilyV4 IPFamily = iota // IPv4 address family.
	IPFamilyV6                 // IPv6 address family.
)

IP address family constants.

Directories

Path Synopsis
Command txeh provides a CLI for managing /etc/hosts file entries.
Command txeh provides a CLI for managing /etc/hosts file entries.
cmd
Package cmd implements the txeh command-line interface.
Package cmd implements the txeh command-line interface.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL