CLI tool
mpolden/echoip avatar
mpolden/echoip

echoip: the small Go service behind ifconfig.co

A simple IP address lookup service.

4,356 stars567 forksGoBSD-3-Clause

At a glance

What is it?
A single Go binary that answers IP, country, city and ASN lookups over plain text or JSON, plus a scratch-based Docker image. Worth reading because it shows how little a useful lookup service actually needs.
Who is it for?
echoip is a good fit for a self-hosted address echo on an internal network, a debugging aid, or a starting point for a service that needs country and ASN context without standing up a database server of its own. It is the wrong choice if you need a public endpoint that will not become an open proxy for address lookups, and the wrong choice if you need SLA-backed geolocation accuracy, since the data is the free MaxMind GeoLite tier downloaded with your own credentials.
Can I use it commercially?
Yes. BSD-3-Clause 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 last received commits 48 days ago.
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.

From daily snapshots

Star history

  • mpolden/echoip4,356 ★ · +3
Star history
Repository2026-10-072026-10-23
mpolden/echoip4.4k4.4k

3 Stars in the last 16 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

A plain text address reply that five clients agree on

The README opens with the smallest thing a service of this kind can do: print the address of the machine that asked, and nothing else. Every command-line HTTP client gets the same reply.

bash
$ curl ifconfig.co
127.0.0.1

The list also covers `http`, `wget`, `fetch`, and `bat -print=b ifconfig.co/ip`. Supporting `bat` matters more than it first appears, because a shell prompt that pipes `curl` through a pager is the main way people hit these services interactively. A response with the wrong content type or an HTML body breaks that pipeline, so the service returns a bare address and a newline.

The domain name is part of the product. The README lists an easy to remember domain name as the first feature, which is a reasonable claim for a service whose entire interface is one URL: `ifconfig.co` reads like a shell command, so a human can recall it without a bookmark.

Country, city and ASN come from three downloaded MaxMind files

The README states it plainly: `echoip` uses the MaxMind GeoIP databases to show additional information about IP addresses, such as registered country/city and ASN details.

bash
$ curl ifconfig.co/country
Elbonia

$ curl ifconfig.co/country-iso
EB

Those databases are not committed to the repository. The Makefile lists `GeoLite2-City`, `GeoLite2-Country` and `GeoLite2-ASN` as targets under `geoip-download`, and both credentials are checked before anything is fetched, so a missing key produces a make error naming the two variables rather than a silently empty database.

bash
GEOIP_LICENSE_KEY=<key> MAXMIND_ACCOUNT_ID=<account-id> make geoip-download

After the download the Makefile renames the extracted files to `data/city.mmdb`, `data/country.mmdb` and `data/asn.mmdb`, which lines up with the flag names in the usage output: `-a` for the ASN database, `-c` for city, `-f` for country. The JSON endpoint reports all of it in one object, including `ip_decimal` for the address as an integer.

Dependency weight is low. `go.mod` names a single direct requirement, `github.com/oschwald/geoip2-golang v1.13.0`, and declares Go 1.24. The trade-off to keep in mind is data quality rather than code quality: the free GeoLite tier is what these flags point at by default, so country and city answers are only as current as the files you download.

Port testing is opt-in and refuses to look up other addresses

The `/port` endpoint turns the service into a reachability check for one TCP port on the caller's own address.

bash
$ curl ifconfig.co/port/80
{
  "ip": "127.0.0.1",
  "port": 80,
  "reachable": false,
  "status": "refused"
}

Two details are worth pausing on. The README says all endpoints except `/port` accept a custom address through the `?ip=` query parameter, and this one is deliberately excluded, so a public instance cannot be used to probe arbitrary hosts. It is also disabled unless you pass `-p`, which the usage block describes as `Enable port lookup`.

The response shape is flat on purpose: address, port, a boolean, and a status word such as `refused`. That is enough for a shell script to branch on without pulling in a JSON parser, and it keeps the handler small enough to read in one sitting.

The Makefile holds the project's own quality bar

Installing from source takes one line, and the README is explicit about the prerequisite: compiling requires the Golang compiler.

bash
go install github.com/mpolden/echoip/...@latest

The more interesting file is the Makefile, because its default target encodes what the maintainer considers done.

bash
go test ./...
go vet ./...
gofmt -w .

The `all` target is `checkfmt vet test install`, in that order. `checkfmt` does not rewrite anything: it shells out to `gofmt -l .` and fails with a message telling you to try `make fmt` to fix it automatically. So a plain `make` refuses to install a binary from code that is not gofmt-clean, and the repository tree carries a `.github/` directory whose workflow badge sits at the top of the README.

A scratch image that ships no databases

A published container image is available on Docker Hub as `mpolden/echoip`.

bash
docker pull mpolden/echoip

The Dockerfile is worth reading for what it leaves out. It is two stages: the build stage starts `FROM golang:1.24`, sets `CGO_ENABLED=0` with a comment saying the build must work without cgo because libc is unavailable in the runtime image, and runs `make`. The runtime stage is `FROM scratch`, exposes 8080, and copies exactly two things, the compiled binary and the `html` template directory.

No database files are copied, because nothing in the build creates them and `geoip-download` needs credentials the build does not have. The practical result is that the image answers the address and port endpoints out of the box, and the country, city and ASN endpoints need paths supplied at run time through `-c`, `-f` and `-a`. A scratch base also means there is no shell in the container if the entrypoint fails to start.

The Makefile closes the loop with `docker-build`, which tags `mpolden/echoip`, and `docker-test`, which runs the image detached, reads the mapped port back with `docker port`, curls it with a five second timeout, and stops the container.

The trust decisions live in two flags and nowhere else

The usage block is the entire configuration surface, and two entries in it decide how much you trust the network in front of the service.

bash
Usage of echoip:
  -H value
        Header to trust for remote IP, if present (e.g. X-Real-IP)
  -L    Disable custom IP lookup

`-H` exists for running behind a reverse proxy, where the socket address is the proxy and the real client address arrives in a header. Pass it only when nothing outside your proxy can set that header, otherwise any caller can claim any address and every downstream decision based on geolocation becomes fiction. `-L` is the counterweight on the other side: the README advertises `?ip=` on every endpoint except `/port`, and this flag turns that off so responses always describe the connection.

The remaining flags are cosmetic or operational: `-C` sets the response cache size and zero disables it, `-P` enables profiling handlers, `-r` turns on reverse hostname lookups, `-s` shows a sponsor logo, `-l` sets the listening address with a default of `:8080`, and `-t` points at the template directory. That the whole list fits on a screen is a fair summary of the project: this is a service with one data source and one trust boundary.

The repository publishes no GitHub releases, so there is no tagged artifact to pin and no changelog to read. The README's claim that the service is faster than ifconfig.me and adds IPv6 support is a comparison the project does not substantiate anywhere in the tree; IPv6 you can verify yourself by passing `-4` or `-6` to your client, as the README instructs.

Editorial conclusion

echoip is a good fit for a self-hosted address echo on an internal network, a debugging aid, or a starting point for a service that needs country and ASN context without standing up a database server of its own. It is the wrong choice if you need a public endpoint that will not become an open proxy for address lookups, and the wrong choice if you need SLA-backed geolocation accuracy, since the data is the free MaxMind GeoLite tier downloaded with your own credentials. The last push was on 2026-09-05 and the repository publishes no GitHub releases, so pin a container tag rather than tracking master. Start with `docker pull mpolden/echoip` and `-l :8080`, then add `-c`, `-f` and `-a` once you have run `make geoip-download`.

Frequently asked questions

How do I run my own echoip instance instead of using ifconfig.co?

Install it with `go install github.com/mpolden/echoip/...@latest`, which requires the Golang compiler, or pull the published image with `docker pull mpolden/echoip`. The binary listens on `:8080` by default, so pass `-l` to change the address.

Does echoip let me look up an IP address other than my own?

Yes. The README states that all endpoints except `/port` can return information about a custom address supplied through the `?ip=` query parameter. The `-L` flag disables that custom lookup, and `/port` always probes the connection's own address so a public instance cannot be used to scan other hosts.

Where does echoip get its geolocation data?

From the MaxMind GeoIP databases, which are not stored in the repository. The Makefile target `geoip-download` fetches GeoLite2-City, GeoLite2-Country and GeoLite2-ASN using `GEOIP_LICENSE_KEY` and `MAXMIND_ACCOUNT_ID`, then renames them to `city.mmdb`, `country.mmdb` and `asn.mmdb`, which you pass to the binary through `-c`, `-f` and `-a`.

How does echoip compare with ifconfig.me?

The README claims it is faster and adds IPv6 support, but the repository contains no benchmark or measurements backing that comparison, so treat it as an unverified claim. IPv6 you can check directly by passing `-4` or `-6` to the client, as the README describes.

Official sources

  1. Issues
  2. License: BSD-3-Clause
  3. mpolden/echoip on GitHub
  4. Project website
  5. README
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/mpolden-echoip.svg)](https://hysenlabs.com/projects/mpolden-echoip)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/mpolden-echoip.svg)](https://hysenlabs.com/projects/mpolden-echoip)

Was this useful?