Summary
The public.ecr.aws/supabase/postgres image contains two different zoneinfo databases, and postgres is compiled against the older one. /usr/share/zoneinfo is Alpine's current tzdata-2026d, but the server is built with --with-system-tzdata pointing at a Nix-provided tzdata-2025b. Any zone whose rules changed between 2025b and 2026d converts incorrectly inside Postgres, while date in the same container gets it right.
It fails silently. There is no warning, and pg_timezone_names is a view over the same stale directory, so nothing inside the database can reveal the skew.
Affected versions
Confirmed on 17.6.1.136 and on the newest release, 17.6.1.173. Both use the identical nix store path, so the pin has not moved across any release in that range.
$ docker run --rm --entrypoint pg_config public.ecr.aws/supabase/postgres:17.6.1.173 --configure | tr ' ' '\n' | grep tzdata
'--with-system-tzdata=/nix/store/q42y30akm86ph3pzzdrp5qyrm7pg6sqy-tzdata-2025b/share/zoneinfo'
$ docker run --rm --entrypoint apk public.ecr.aws/supabase/postgres:17.6.1.173 list -I tzdata
tzdata-2026d-r0 x86_64 {tzdata} (Public-Domain) [installed]
The next release will carry the same pin unless it is bumped: flake.lock on develop resolves nixpkgs to fb7944c (2026-01-02), and tzdata at that revision is still 2025b.
Reproduction
British Columbia, Alberta and the Northwest Territories moved to permanent summer time, so their winter offsets changed. Morocco moved to permanent UTC on 2026-09-20.
select z, ('2027-01-15 12:00'::timestamp at time zone z) at time zone 'UTC' as utc
from unnest(array['America/Vancouver','America/Edmonton','America/Inuvik','Africa/Casablanca','America/New_York']) z;
| zone |
returned |
correct (tzdata 2026d) |
| America/Vancouver |
2027-01-15 20:00 |
19:00 (changed in 2026b) |
| America/Edmonton |
2027-01-15 19:00 |
18:00 (2026c) |
| America/Inuvik |
2027-01-15 19:00 |
18:00 (2026d) |
| Africa/Casablanca |
2027-01-15 11:00 |
12:00 (2026c) |
| America/New_York |
2027-01-15 17:00 |
17:00 (control, unchanged) |
The same container disagrees with itself:
$ docker run --rm --entrypoint bash public.ecr.aws/supabase/postgres:17.6.1.173 -c '
NIX=/nix/store/q42y30akm86ph3pzzdrp5qyrm7pg6sqy-tzdata-2025b/share/zoneinfo
for z in America/Vancouver America/Edmonton; do
echo "$z usr=$(TZ=:/usr/share/zoneinfo/$z date -d "2027-01-15 12:00" +%z)" \
" nix=$(TZ=:$NIX/$z date -d "2027-01-15 12:00" +%z)"
done'
America/Vancouver usr=-0700 nix=-0800
America/Edmonton usr=-0600 nix=-0700
Which zones are affected
34 zone files differ between the two directories. I compared every one of them at 02:30 and 12:00 local time on every day from 2026-09-18 to 2028-12-31. These give wrong future instants inside Postgres:
America/Vancouver (and Canada/Pacific): an hour off every winter from 2026-11-01
America/Edmonton (and Canada/Mountain, America/Yellowknife): an hour off every winter from 2026-11-01
America/Inuvik: an hour off every winter from 2026-11-01
Africa/Casablanca and Africa/El_Aaiun: an hour off almost all year from 2026-09-20
The other 26 files differ only in past timestamps. Europe/Chisinau also moved its transition times by an hour (2026a), which only matters inside the transition hour.
Impact
Applications that convert a local wall clock to an instant server-side, with (date + time) AT TIME ZONE tz, which is the documented way to schedule from user input, persist the wrong instant with no error. Because the value is materialised at write time, every downstream consumer inherits it: reminders, notification scheduling and iCalendar feeds. The rows stay wrong after tzdata is eventually fixed, and for one-off events the original wall clock can't be recovered once the zone rules move, so the corruption isn't fully repairable after the fact.
Clients are usually correct, since Android/Hermes and recent browsers already ship 2026c or 2026d. So the database disagrees with the device that produced the input, which makes it look like an application bug rather than a platform one. It cost us a full misdiagnosis cycle.
Many current Node builds are also on 2025b (process.versions.tz), so a Node-based test agrees with the stale database by coincidence and can't detect this.
Suggested fix
Move the nix tzdata pin forward so the server is built against the same generation the image already ships. Ideally, also assert at build time that the two agree. A single conversion check on a recently changed zone would have caught this.
Workarounds (for anyone who finds this issue)
- Overwriting the files under the nix store path inside a running container works, but it is lost when the container is recreated and can't be applied to a hosted project.
- At the application level, the most robust option is to store the local wall clock and zone alongside the instant, so rows can be recomputed once tzdata is fixed. It has to be in place before the tzdata moves for the recompute to be lossless. For a zone whose new rule is a fixed offset, doing the conversion yourself for that zone also works in the meantime.
Environment
public.ecr.aws/supabase/postgres:17.6.1.173 (and :17.6.1.136), PostgreSQL 17.6, Alpine Linux, linux/amd64, Docker on Windows 11.
Summary
The
public.ecr.aws/supabase/postgresimage contains two different zoneinfo databases, andpostgresis compiled against the older one./usr/share/zoneinfois Alpine's currenttzdata-2026d, but the server is built with--with-system-tzdatapointing at a Nix-providedtzdata-2025b. Any zone whose rules changed between 2025b and 2026d converts incorrectly inside Postgres, whiledatein the same container gets it right.It fails silently. There is no warning, and
pg_timezone_namesis a view over the same stale directory, so nothing inside the database can reveal the skew.Affected versions
Confirmed on
17.6.1.136and on the newest release,17.6.1.173. Both use the identical nix store path, so the pin has not moved across any release in that range.The next release will carry the same pin unless it is bumped:
flake.lockondevelopresolvesnixpkgstofb7944c(2026-01-02), andtzdataat that revision is still2025b.Reproduction
British Columbia, Alberta and the Northwest Territories moved to permanent summer time, so their winter offsets changed. Morocco moved to permanent UTC on 2026-09-20.
The same container disagrees with itself:
Which zones are affected
34 zone files differ between the two directories. I compared every one of them at 02:30 and 12:00 local time on every day from 2026-09-18 to 2028-12-31. These give wrong future instants inside Postgres:
America/Vancouver(andCanada/Pacific): an hour off every winter from 2026-11-01America/Edmonton(andCanada/Mountain,America/Yellowknife): an hour off every winter from 2026-11-01America/Inuvik: an hour off every winter from 2026-11-01Africa/CasablancaandAfrica/El_Aaiun: an hour off almost all year from 2026-09-20The other 26 files differ only in past timestamps.
Europe/Chisinaualso moved its transition times by an hour (2026a), which only matters inside the transition hour.Impact
Applications that convert a local wall clock to an instant server-side, with
(date + time) AT TIME ZONE tz, which is the documented way to schedule from user input, persist the wrong instant with no error. Because the value is materialised at write time, every downstream consumer inherits it: reminders, notification scheduling and iCalendar feeds. The rows stay wrong after tzdata is eventually fixed, and for one-off events the original wall clock can't be recovered once the zone rules move, so the corruption isn't fully repairable after the fact.Clients are usually correct, since Android/Hermes and recent browsers already ship 2026c or 2026d. So the database disagrees with the device that produced the input, which makes it look like an application bug rather than a platform one. It cost us a full misdiagnosis cycle.
Many current Node builds are also on 2025b (
process.versions.tz), so a Node-based test agrees with the stale database by coincidence and can't detect this.Suggested fix
Move the nix
tzdatapin forward so the server is built against the same generation the image already ships. Ideally, also assert at build time that the two agree. A single conversion check on a recently changed zone would have caught this.Workarounds (for anyone who finds this issue)
Environment
public.ecr.aws/supabase/postgres:17.6.1.173(and:17.6.1.136), PostgreSQL 17.6, Alpine Linux,linux/amd64, Docker on Windows 11.