Open-source project
pradt2/always-online-stun avatar
pradt2/always-online-stun

always-online-stun: a STUN server list that is checked every hour

A list of publicly available STUN servers, refreshed every hour.

1,048 stars90 forksRustMIT

At a glance

What is it?
This repository is not a STUN implementation but a continuously refreshed list of publicly available STUN servers, published as plain text files over GitHub raw URLs so an application can fetch a working server on demand. It is written in Rust, MIT licensed, and it ships a geoip_cache.txt so a client can pick the nearest server rather than the first one.
Who is it for?
always-online-stun suits a developer building a WebRTC or NAT traversal client who needs a public STUN server to point at and cannot keep a hardcoded list alive, because the list is probed hourly and the removal path for an unwanted server is a GitHub issue.
Can I use it commercially?
Yes. MIT 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 Rust, according to GitHub's language statistics.

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

From daily snapshots

Star history

  • pradt2/always-online-stun1,048 ★ · +1
Star history
Repository2026-10-212026-11-07
pradt2/always-online-stun1k1k

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

Editorial analysis

A data file with a cron job behind it

The repository description says it plainly: a list of publicly available STUN servers, refreshed every hour. The README opens with the question it exists to answer, whether there is a regularly updated, comprehensive list of publicly available STUN servers, and answers itself.

So what this is not is immediately clear. There is no STUN server here, and no client library that you link against to do STUN. The repository is a data set plus the tooling that keeps it honest, written in Rust with a Cargo.toml, a src/ directory, a Cargo.lock and a rust-toolchain file.

The dependency list explains the job. reqwest with rustls-tls and json features is the HTTP client used to probe candidates, tokio with the fs, macros, net and rt features is the async runtime, async-trait and futures handle the async traits, and stun-format with its fmt feature is the library that formats STUN messages. rand is there for the shuffling, serde_json for parsing responses, and log with pretty_env_logger for output. The edition is 2018 and the version is 0.1.0, so this is a young tool.

The distribution mechanism is deliberately dull. The published artefacts are plain text files served from GitHub raw URLs, which means consuming them needs nothing more than an HTTP GET and a text split. That is the whole appeal for a client that just needs one working address.

The README's JavaScript example shows the intended first use, fetching the geolocation cache and the IPv4 list and picking the closest server.

javascript
const GEO_LOC_URL = "https://raw.githubusercontent.com/pradt2/always-online-stun/master/geoip_cache.txt";
const IPV4_URL = "https://raw.githubusercontent.com/pradt2/always-online-stun/master/valid_ipv4s.txt";
const GEO_USER_URL = "https://geolocation-db.com/json/";
const geoLocs = await(await fetch(GEO_LOC_URL)).json();
const { latitude, longitude } = await(await fetch(GEO_USER_URL)).json();

There is no install step for a consumer. You fetch a text file, and the Rust in this repository is what produces it.

Sixteen files, because one list is never enough

The top level of the repository shows how many dimensions the project has to publish.

There is valid_hosts.txt, valid_ipv4s.txt and valid_ipv6s.txt as the base set, then the same three with a _tcp suffix, giving valid_hosts_tcp.txt, valid_ipv4s_tcp.txt and valid_ipv6s_tcp.txt. Then the NAT testing set: valid_nat_testing_hosts.txt, valid_nat_testing_ipv4s.txt, valid_nat_testing_ipv6s.txt, and their three _tcp counterparts. Alongside them sit geoip_cache.txt, which maps servers to coordinates, and candidates.txt, which is the input list of servers to check.

The multiplication is along two independent axes. The first is IP version, IPv4 or IPv6. The second is transport, UDP or TCP, which matters because a client behind a restrictive network may have UDP blocked and TCP open. The third axis is capability, plain NAT detection or the fuller NAT behaviour testing set.

That is why the README's usage advice has three links rather than one. Hard-code valid_hosts.txt into your application for the general case, and use valid_ipv4s.txt or valid_ipv6s.txt if you would rather not rely on DNS resolution. The DNS point is the reason the IP lists exist at all: if you already know the address, you can skip a resolution step that may itself be the thing failing.

The repository also carries an examples/ directory with simple_read.rs, simple_write.rs, stun_client_udp.rs and stun_client_tcp.rs, so the four published shapes have runnable counterparts.

What qualifies a server for each list

The FAQ explains the qualification rules, and the distinction between the two families of list is the most important thing in it.

The valid_nat_testing_* lists contain servers that should be capable of both NAT detection and behaviour testing. These capabilities roughly correspond to RFC5780 and, implicitly, to RFC5389. To qualify, a server has to correctly respond to an RFC5389 BINDING request and provide the OTHER-ADDRESS attribute in the response. The presence of the OTHER-ADDRESS attribute is the spec-compliant way to advertise that a STUN server can be used for NAT behaviour tests.

Then the honest caveat. At the moment, no actual verification of the NAT behaviour testing capabilities is carried out. The project relies on the STUN server maintainers to set the OTHER-ADDRESS attribute only if their server supports NAT behaviour testing, and invites you to open an issue if you need a stronger guarantee.

That is a trust-based qualification rather than a tested one, and it is the single most important limitation of the project. A server that advertises OTHER-ADDRESS and then does not behave correctly for RFC5780 will pass this list's checks and fail yours.

The other valid_* lists contain servers capable of NAT detection only, and these are much bigger lists, because only a small fraction of servers is configured to provide the full NAT testing capabilities. To qualify for these, a server has to correctly respond to an RFC5389 BINDING request. That is a much easier bar to meet, and the list sizes reflect it.

The IP versions and transports tested are 4 and 6, over UDP and TCP.

Why the lists are shuffled on every check

There is a FAQ entry that explains a design decision which looks like a bug, and it is the kind of decision that only makes sense once you know who the consumers are.

The complaint is that the lists are shuffled on each check. The answer: lazy or inconsiderate devs will tend to just grab the top-most link from the list, and by shuffling the list, the author ensures that we don't end up spamming the same host forever.

That is a load-shaping decision aimed at the public servers rather than at the client. If every application that used this list took the first entry, the top of the list would receive traffic from every consumer of the project, while the rest of the list sat unused. Shuffling spreads that load across the whole set on every refresh, and since the refresh is hourly, the ordering changes before a client has a reason to care about it.

It also means a client that caches a specific entry is making a worse choice than one that re-reads, and the project is nudging its users toward the latter behaviour without documenting it as a requirement.

The nearest-server approach in the example leans on the same file set plus geoip_cache.txt, which maps each server address to coordinates, so a client can pick the closest entry rather than the first. The example fetches the geolocation cache, fetches the caller's own location from a geolocation service, then reduces over the IPv4 list computing a Euclidean distance in latitude and longitude.

The caveat is that the distance is computed in degrees, so it is a rough planar approximation, and the caller's location comes from a third-party service that has to be reachable for the trick to work at all.

Adding a server, and getting removed

Two FAQ entries cover the two directions of the contributor relationship, and they are the practical interface to the project.

For adding: the list is in candidates.txt, and you are invited to create a PR adding more servers, or just to open an issue and list them there. A candidate list rather than a hand-edited published list is the right structure, because everything in the published files has been probed and confirmed, and everything in candidates.txt is a proposal awaiting that check.

For removal: if your server is on the list and you do not like it, open an issue, and it will be removed from the automated checks within 24 hours.

The 24 hour bound is a commitment worth noting. This is a project that pokes other people's servers, and the operators of those servers should not have to file a request and wait indefinitely. A stated removal window is the minimum courtesy an aggregate list owes the people whose machines it uses, and the fact that it is written into the README rather than left implicit is a good sign about how the project is run.

The refresh cadence is hourly, and the README says you can see the timestamp of the last check in the commit message. So the history of the list is a commit history, and each entry carries the moment it was last confirmed alive. That is a better provenance model than a hand-maintained list could offer, and it is what makes the published files trustworthy enough to hard-code a link to.

The repository has no GitHub releases, so the list is versioned by commit rather than by tag, and the last push was on 2026-10-15.

Editorial conclusion

always-online-stun suits a developer building a WebRTC or NAT traversal client who needs a public STUN server to point at and cannot keep a hardcoded list alive, because the list is probed hourly and the removal path for an unwanted server is a GitHub issue. It is the wrong tool if you need a STUN server you control, since these are public servers with no SLA and no guarantee of staying online, and it is the wrong choice if you need NAT behaviour testing, since the README states that no actual verification of those capabilities is carried out. Before you depend on it, read the qualification rules for the valid_nat_testing lists, and note that the lists are shuffled on every check so that clients do not all converge on the same host.

Frequently asked questions

What is always-online-stun and how do I use it?

It is a list of publicly available STUN servers refreshed hourly and published as plain text files. The README suggests hard-coding the link to valid_hosts.txt into your application, and using valid_ipv4s.txt or valid_ipv6s.txt if you do not want to rely on DNS resolution.

How often is the STUN server list refreshed?

Hourly. The README says you can see the timestamp of the last check in the commit message.

What does a server have to support to appear in the NAT testing lists?

It has to correctly respond to an RFC5389 BINDING request and provide the OTHER-ADDRESS attribute in the response, which is the spec-compliant way to advertise support for NAT behaviour tests. The plain valid_* lists only require a correct BINDING response.

Is the NAT testing capability actually verified?

No. The README states that at the moment no actual verification of the NAT behaviour testing capabilities is carried out, and that the project relies on the STUN server maintainers to set the OTHER-ADDRESS attribute only if their server supports it. It asks you to open an issue if you need a stronger guarantee.

How do I get my server removed from the list?

Open an issue, and the README says it will be removed from the automated checks within 24 hours. New servers are added through a pull request against candidates.txt or by opening an issue listing them.

Official sources

  1. Issues
  2. License: MIT
  3. pradt2/always-online-stun on GitHub
  4. 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/pradt2-always-online-stun.svg)](https://hysenlabs.com/projects/pradt2-always-online-stun)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/pradt2-always-online-stun.svg)](https://hysenlabs.com/projects/pradt2-always-online-stun)

Was this useful?