Skip to content

Commit caf645a

Browse files
committed
Normalize GeoIP host binding for Railway
1 parent cc8de6c commit caf645a

5 files changed

Lines changed: 16 additions & 9 deletions

File tree

apps/geoip/.env.default

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# GeoIP service
22
GEOIP_DB_DIR=/data/geoip
3-
HOST=::
3+
HOST=0.0.0.0
44
PORT=8080
55

66
# Local API -> GeoIP service

apps/geoip/README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ MAXMIND_LICENSE_KEY=<secret>
3030
MAXMIND_EDITION_IDS=GeoLite2-City GeoLite2-ASN
3131
GEOIP_DB_DIR=/data/geoip
3232
GEOIP_UPDATE_INTERVAL_HOURS=24
33-
HOST=::
33+
HOST=0.0.0.0
3434
PORT=8080
3535
```
3636

@@ -59,7 +59,7 @@ GEOIP_SERVICE_URL=http://localhost:8083
5959

6060
Deploy this app as its own Railway service named `geoip`.
6161

62-
The service uses Hypercorn and binds both `0.0.0.0` and `::` by default so Railway's public deployment healthchecks and private networking can both reach it.
62+
The service uses Hypercorn and binds to `0.0.0.0` by default. Wildcard values like `HOST=::` are normalized to the same single bind so Linux container runtimes do not fail with `address already in use`.
6363

6464
Use `/live` as the Railway deployment healthcheck path. `/health` remains a readiness endpoint and can return `503` until the MaxMind databases have been downloaded and loaded.
6565

@@ -70,7 +70,7 @@ The service now starts serving `/live` immediately while the initial MaxMind boo
7070
- `current_update_started_at`: when the active update began
7171
- `last_update_error`: the latest refresh/bootstrap failure, if any
7272

73-
Railway environment variable values should be entered as raw values without wrapping quotes. For example, use `HOST=::`, not `HOST="::"`.
73+
Railway environment variable values should be entered as raw values without wrapping quotes. For example, use `HOST=0.0.0.0`, not `HOST="0.0.0.0"`.
7474

7575
Railway logs will now include `geoipupdate` output, which makes it easier to confirm whether the MaxMind databases are actively downloading.
7676

apps/geoip/src/config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ def from_env(cls) -> "Settings":
4949
license_key=os.getenv("MAXMIND_LICENSE_KEY", "").strip(),
5050
edition_ids=_split_edition_ids(os.getenv("MAXMIND_EDITION_IDS")),
5151
db_dir=Path(os.getenv("GEOIP_DB_DIR", "/data/geoip")),
52-
host=os.getenv("HOST", "::").strip() or "::",
52+
host=os.getenv("HOST", "0.0.0.0").strip() or "0.0.0.0",
5353
update_interval_hours=int(os.getenv("GEOIP_UPDATE_INTERVAL_HOURS", "24")),
5454
port=int(os.getenv("PORT", "8080")),
5555
)

apps/geoip/src/main.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,10 @@ async def initialize_databases(manager: GeoIPDatabaseManager) -> None:
3838

3939
def build_bind_addresses(host: str, port: int) -> list[str]:
4040
normalized_host = host.strip()
41-
if not normalized_host or normalized_host == "::":
42-
return [f"0.0.0.0:{port}", f"[::]:{port}"]
41+
# Binding both 0.0.0.0 and [::] can conflict on Linux when the IPv6 socket
42+
# also accepts IPv4 traffic. Normalize wildcard hosts to a single safe bind.
43+
if not normalized_host or normalized_host in {"::", "0.0.0.0"}:
44+
return [f"0.0.0.0:{port}"]
4345

4446
if ":" in normalized_host and not normalized_host.startswith("["):
4547
return [f"[{normalized_host}]:{port}"]

apps/geoip/tests/test_main.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,8 +122,13 @@ def test_live_returns_200_while_initialization_is_still_running() -> None:
122122
assert health_response.json()["update_in_progress"] is True
123123

124124

125-
def test_build_bind_addresses_defaults_to_dual_stack_for_railway() -> None:
126-
assert build_bind_addresses("::", 8080) == ["0.0.0.0:8080", "[::]:8080"]
125+
def test_build_bind_addresses_normalizes_wildcard_hosts_to_single_bind() -> None:
126+
assert build_bind_addresses("::", 8080) == ["0.0.0.0:8080"]
127+
assert build_bind_addresses("0.0.0.0", 8080) == ["0.0.0.0:8080"]
128+
129+
130+
def test_build_bind_addresses_preserves_explicit_ipv6_host() -> None:
131+
assert build_bind_addresses("2001:db8::1", 8080) == ["[2001:db8::1]:8080"]
127132

128133

129134
def test_lookup_returns_payload() -> None:

0 commit comments

Comments
 (0)