Open-source project
cunnie/sslip.io avatar
cunnie/sslip.io

sslip.io: a DNS server that turns a hostname into the IP address inside it

Golang-based DNS server which maps DNS records with embedded IP addresses to those addresses.

1,198 stars108 forksGoApache-2.0

At a glance

What is it?
sslip.io is a Go DNS server that answers A and AAAA queries for names like 127-0-0-1.sslip.io with the address embedded in the name, and it can be pointed at your own namespace for air-gapped setups. This looks at the flags that shape it, the safety switch for public addresses, and what it deliberately does not do.
Who is it for?
sslip.io fits a developer who needs a hostname that resolves to a specific address without touching DNS records, and it fits a team in an air-gapped environment that wants sslip.io style lookups inside its own domain, with the `-nameservers`, `-addresses` and `-ptr-domain` flags. It is the wrong tool if you need a general purpose resolver, because the project states plainly that these nameservers are not recursive and never resolve google.com.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository received new commits within the last day.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A hostname that carries its own address

sslip.io is a DNS server that maps specially-crafted DNS A records to IP addresses, so 127-0-0-1.sslip.io resolves to 127.0.0.1. The project describes itself as similar to, and inspired by, the now-defunct xip.io.

The problem it solves is small and real. During development you often want a hostname that points at a specific machine, because a service does something different based on the Host header, or a certificate has to be issued for that name, or a test asserts on the origin. Editing DNS records for each of those is friction, and in a containerised or cloud environment it can be impossible without access to the zone.

The mechanism is encoding. The address in the name is written with dashes instead of dots, 192.168.0.1 becomes 192-168-0-1, and the server extracts it and answers with it. The same trick works for IPv6, which is why the server handles AAAA records as well as A.

The audience is developers and the operators of the public service. The project makes the distinction itself: the repository targets developers, while the website at nip.io targets users, and the public nameservers are the hosted instance of the same code.

The code is Go, the module is named `xip`, the licence is Apache-2.0, and the last push to the repository was on 2026-10-14.

Authoritative for its own names, and never recursive

The most important operational fact in the project documentation is a warning rather than a feature. When you run your own nameservers with the `-nameservers` flag, the documentation says they are not general-purpose nameservers, that they will not look up google.com, that they are not recursive, and that you should never configure a client to use these nameservers.

That is the correct design for this kind of server. It answers authoritatively for a namespace it controls and refuses to forward anything else, which means it cannot be used as an open resolver and cannot be turned into a DNS amplification reflector by pointing a client at it.

The rest of the DNS behaviour is documented with the same bluntness. The server binds to both UDP and TCP, and the TCP side was the subject of the 6.0.1 release, whose title is "Stop TCP Queries from Hanging". The same SOA record is returned for every domain, with its MNAME always set to `ns-01.nip.io`, overridable with the `-soa` flag. The MX records are hard-coded to the queried hostname with a preference of 0, except for `sslip.io` itself, which has custom MX records to enable email delivery to ProtonMail. There are no SRV records.

The record model is therefore narrow on purpose. If your application depends on SRV records or on per-zone SOA values, this server will not give them to you.

Running it locally and asking it a question

The quick start is four commands. You clone the repository, tidy the modules, and run the server, which needs root because it binds to the privileged port 53:

bash
git clone https://github.com/cunnie/sslip.io.git
cd sslip.io
go mod tidy
sudo go run .
 # sudo is required to bind to privileged port 53

Then, from another window, you query it:

bash
dig @localhost 192.168.0.1.sslip.io +short
 # should return "192.168.0.1"

If you cannot or do not want to bind port 53, the `-port` flag overrides the default, and the documentation gives the example of running on port 9553 and querying with `dig @localhost 127.0.0.1.sslip.io -p 9553`. That flag is described as especially useful when running as a non-privileged user, with the error `listen udp :53: bind: permission denied` given as the symptom.

The project uses Ginkgo and Gomega for its tests, and the test commands are documented separately. To test the code rather than the production nameservers, the documentation says to run `ginkgo -r -p --skip-package=spec`, and to test the production nameservers there is a separate `spec/` suite run with `DOMAINS=nip.io,sslip.io ginkgo -r -p spec/`. The `spec/` directory holds the tests for the live service rather than for the code, and the Quick Start test sequence installs the ginkgo binary and the gomega modules first.

Pointing it at your own namespace with two flags

The feature that makes sslip.io useful beyond a public service is running it against your own domain, and the project walks through the whole setup. The scenario in the documentation is an organisation that wants a subdomain of its own domain to do sslip.io-style lookups, with two servers set aside for it, one on the IPv4 address 10.8.8.8 and one on the IPv6 address fc88::. The subdomain is delegated to those two nameservers, and the server is then run on each of them with the same four flags.

The two flags work together. `-nameservers` replaces the default NS records, which the documentation names as `ns-00.nip.io`, `ns-01.nip.io` and `ns-ovh.sslip.io`, and `-addresses` sets the A and AAAA records for those nameservers, since a nameserver without an address record cannot be delegated to. The `-addresses` flag accepts several addresses for one host, so a single name can carry a list.

Two more flags shape the output. `-ptr-domain` sets the domain used for reverse lookups, so `dig -x 127.0.0.1` returns `127-0-0-1.ip.example.com`, and the documentation recommends making the forward and reverse lookups match. The `-blocklistURL=file:///dev/null` in the example is how you point the blocklist at nothing, which is what you want in a closed environment where the default remote list cannot be fetched.

The public address switch is the security decision

Two flags decide how the server behaves towards the world, and one of them deserves careful reading before you deploy it.

The `-public` flag controls whether public addresses are resolved. The documentation suggests that if you are unsure, set `-public=false`, which means only private IP addresses will resolve, giving examples such as 10.9.9.30, 192.168.0.1 and 169.254.169.254.

The reason given is a concrete attack. The documentation asks you to imagine running a bank whose nip.io-style subdomain is ip.wellsfargo.com, and warns against letting a phisher spin up a VM with a public IP address, obtain a certificate for that IP address's hostname such as 52-0-56-137.ip.wellsfargo.com, and use it to phish customers. Setting `-public=false` prevents that scenario, because the name will not resolve to a publicly routable address at all.

That is a well designed control, but note the direction it pushes: for anything not on a private range, the default is the permissive one. Anyone running a nameserver under their own domain should decide explicitly rather than inherit the default.

The other flag is the blocklist. The default blocklist is fetched from a URL in the repository, and the documentation is honest that it is not a show-stopper: if the DNS server cannot download the blocklist, it logs a message and continues to serve DNS queries. A blocklist that fails open is a blocklist you cannot rely on, so treat it as a convenience rather than a control.

The Docker image and the multi-platform story

The project states that the easiest way to run the nameserver is the official Docker image, cunnie/sslip.io-dns-server, and it is multi-platform, supporting x86_64 as well as ARM64 on AWS Graviton and Apple M1 through M6.

The example runs the same four flags as the bare binary, with the container publishing host port 5553 to container port 53 over UDP so the container can run unprivileged, and with the image name cunnie/sslip.io-dns-server.

Mapping host port 5553 to container port 53 means the server inside still binds 53, but you never need privileges on the host. The documentation then queries the NS records and shows the expected answer section with the two delegated nameservers, and an additional section with their A and AAAA records, so you can confirm the delegation works end to end.

The repository carries the operational pieces for running the public service: a `Docker/` directory, a `k8s/` directory that also holds the HTML content of the nip.io website, a `terraform/` directory, `dnstap.go` and its tests, a `revoked_ssl/` directory, and integration tests named for the areas they cover, including TCP, metrics, record flags and speed.

Apache-2.0, a Go module on 1.26, and a 6.0 line that changed the defaults

The licence is Apache-2.0, with the licence file at the repository root. The code is a Go module named `xip` requiring Go 1.26.0, and it pulls in very little: Ginkgo and Gomega for tests, and `golang.org/x/net`. The dependency list in the manifest is short, which is what you would expect from a server whose entire job is parsing a name and answering a query.

The release history is where the recent behaviour changes are, and two of them matter operationally. Version 6.0.0, published on 2026-09-29, is titled "No more hard-coded nameservers, addresses", which is the change that broke older configurations: the defaults that used to be baked in, the `ns-00.nip.io` and `ns-01.nip.io` nameservers and their addresses, are now something you set yourself with the flags. Version 6.0.1, published on 2026-10-02, is titled "Stop TCP Queries from Hanging", which is a fix for clients that open a TCP connection and wait.

Before those, version 5.1.5 in July 2026 was about hosting cost rather than features.

The maintenance picture is healthy on the source side, with the last push on 2026-10-14, but the operational documentation is written for the hosted service as much as for the code, and a self-hosted operator should read the release titles rather than assume the flags in an old blog post still behave the same way.

Editorial conclusion

sslip.io fits a developer who needs a hostname that resolves to a specific address without touching DNS records, and it fits a team in an air-gapped environment that wants sslip.io style lookups inside its own domain, with the `-nameservers`, `-addresses` and `-ptr-domain` flags. It is the wrong tool if you need a general purpose resolver, because the project states plainly that these nameservers are not recursive and never resolve google.com. Before you expose one, set `-public=false`, since the project documents a phishing scenario in which an attacker spins up a machine with a public address and gets a certificate for a hostname under your own domain, and note that release 6.0.0 is titled "No more hard-coded nameservers, addresses", so check the flags against your version.

Frequently asked questions

How do I run the sslip.io DNS server locally?

Clone the repository, run `go mod tidy`, then `sudo go run .`, because binding to privileged port 53 requires root. You can query it with `dig @localhost 192.168.0.1.sslip.io +short`, which should return 192.168.0.1.

Can sslip.io resolve normal domain names like google.com?

No. The project documentation states that its own nameservers are not general-purpose nameservers, that they will not look up google.com, that they are not recursive, and that you should never configure a client to use them. They answer authoritatively for the names they know.

What does the -public flag do in sslip.io?

It controls whether public addresses are resolved. The documentation recommends `-public=false` if you are unsure, which restricts resolution to private IP addresses, and explains that this prevents a phisher from spinning up a VM with a public address and obtaining a certificate for a hostname under your own domain.

How do I serve sslip.io lookups for my own domain?

Delegate a subdomain to your own nameservers and run the server with `-nameservers` set to those names and `-addresses` set to their A and AAAA records, plus `-ptr-domain` for reverse lookups. The project documentation walks through it with a subdomain of an organisation's own domain, served by two nameservers on 10.8.8.8 and fc88::.

What changed in sslip.io 6.0.0?

The release title is "No more hard-coded nameservers, addresses", so the nameserver and address defaults changed; set them explicitly with the `-nameservers` and `-addresses` flags. The following release, 6.0.1, was titled "Stop TCP Queries from Hanging".

Official sources

  1. cunnie/sslip.io on GitHub
  2. Issues
  3. License: Apache-2.0
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/cunnie-sslip-io.svg)](https://hysenlabs.com/projects/cunnie-sslip-io)

Was this useful?