Skip to content

feat: support newer Alpine-based Supabase Postgres images#58

Open
damlayildiz wants to merge 7 commits into
mainfrom
feat/supabase-alpine-images
Open

feat: support newer Alpine-based Supabase Postgres images#58
damlayildiz wants to merge 7 commits into
mainfrom
feat/supabase-alpine-images

Conversation

@damlayildiz

@damlayildiz damlayildiz commented Jul 22, 2026

Copy link
Copy Markdown
Member

Support newer Alpine-based Supabase Postgres images

Summary

Supabase's Postgres images switched their userland from Ubuntu to Alpine for the
current PG17 line (~17.6.1.084 and later). This PR makes the published
sqlitecloud/sqlite-sync-supabase images build cleanly on those newer bases, in
parallel with the existing Ubuntu-based images, and refreshes the pinned base tags.

Key finding: despite the Alpine userland, the PostgreSQL that actually runs on
these images is still a Nix-built, glibc-linked binary (every shipped plugin
needs libc.so.6 + a nix-glibc RUNPATH). So the existing glibc extension artifact
is the correct one
no musl build is required. Only the Dockerfile's build-time
userland tooling (apt-get vs apk) and the pg_config path differ between bases.

Changes

  • docker/postgresql/Dockerfile.supabase.release — made userland-agnostic:
    • detects apk vs apt-get to install the curl used for the download,
    • resolves pg_config dynamically (path differs: /usr/bin on Ubuntu vs the Nix
      default profile on Alpine),
    • installs the same glibc artifact on both; the NIX_PGLIBDIR install logic
      was already portable and is unchanged.
      One file now serves both base families.
  • .github/workflows/main.yml — docker-publish matrix:
    • added a PG17 Alpine row (base 17.6.1.151),
    • added a tag_suffix mechanism so Alpine publishes under distinct tags
      (:17-alpine, :17-alpine-<ver>, :17.6.1.151) with no collision against the
      :17 / :15 aliases,
    • bumped the PG15 base pin 15.8.1.085 → 15.8.1.135 (latest published 15 build;
      still Ubuntu — Supabase kept the 15 line on Ubuntu, 20.04→24.04).
  • Docs:
    • docs/postgresql/quickstarts/supabase-self-hosted.md — documented the
      Ubuntu-vs-Alpine base split, added the :17-alpine image option, updated the
      PG15 base reference to 15.8.1.135.
    • docker/README.md — note that the published release image handles both bases
      automatically, while the local build-from-source flow currently targets Ubuntu.
    • docs/internal/supabase-flyio.md — refreshed the illustrative PG15 base tag
      from 15.8.1.085 to 15.8.1.135 throughout.

Resulting published tags

Row Base image Published tags
PG17 Ubuntu (frozen line) 17.6.1.071 :17, :17-<ver>, :17.6.1.071
PG15 Ubuntu 15.8.1.135 :15, :15-<ver>, :15.8.1.135
PG17 Alpine 17.6.1.151 :17-alpine, :17-alpine-<ver>, :17.6.1.151

The shared :17 / :15 aliases intentionally stay on the Ubuntu builds for now
(non-breaking for existing pins).

Verification (local, arm64)

Built each image with its staged glibc artifact and ran CREATE EXTENSION cloudsync; SELECT cloudsync_version();1.1.2:

  • 17.6.1.071 (Ubuntu / apt branch) — ✅
  • 15.8.1.135 (Ubuntu 24.04 / apt branch) — ✅
  • 17.6.1.151 (Alpine / apk branch) — ✅

Open points (please check)

  1. :17 / :15 alias scheme — deferred by design. Alpine is behind explicit
    -alpine + exact-base tags. Decide whether/when :17 should point at Alpine
    (matching what current Supabase self-hosting pulls), with the Ubuntu build moved
    to e.g. :17-ubuntu.
  2. PG17 Ubuntu row (17.6.1.071) — kept for back-compat. Supabase moved the PG17
    line to Alpine, so this row is frozen on an old Ubuntu base (nothing newer to
    bump to) but still builds and works. Decision: keep it; the only related question
    is the :17 alias in point 1 above.
  3. amd64 not tested locally — only arm64 was validated here. The glibc mechanism
    is arch-independent and CI builds linux/amd64,linux/arm64; the first CI run is
    the real amd64 proof.
  4. Local build-from-source (Dockerfile.supabase, make postgres-supabase-build)
    still targets Ubuntu bases (uses apt-get + hardcoded pg_config). Out of scope
    here, but if the Supabase CLI bundles an Alpine image, this breaks. Compile-in-image
    on Alpine was proven to work — easy follow-up if wanted.
  5. Pin maintenance17.6.1.151 and 15.8.1.135 are exact pins and will age;
    bumping is a manual edit (same as before). Optionally we could resolve the latest
    base tag dynamically in CI, at the cost of build reproducibility. Recommend keeping
    pins.

Test plan

  • CI multi-arch build succeeds for all three Supabase rows (amd64 + arm64).
  • Pulled :17-alpine and :15 images load the extension (cloudsync_version()).
  • Existing :17 / :15 consumers unaffected.

Update — review follow-ups

Three commits on top of the original feature commit.

c4faf47 — review fixes

  • CI job name collision. The two PG17 rows both rendered as docker sqlite-sync-supabase pg17; the job name now includes tag_suffix.
  • apk del curl was a live regression. curl is already present in the real 17.6.1.151 Alpine base — apk add only had to install ca-certificates — so the previous unconditional apk del curl would have stripped curl out of every published Alpine image. It is now removed only if it was not already in the base, and is not installed at all when a beta artifact is staged. The GNU tar install was dropped too; busybox tar extracts the release tarball.
  • CLOUDSYNC_PG_CONFIG is an ARG, not an ENV, so --build-arg actually overrides it (it previously could not) and the empty value no longer leaks into the published image.
  • Missing CLOUDSYNC_VERSION now fails with an actionable message instead of unbound variable.
  • pg_config resolution tries the Nix profiles the Supabase bases ship before falling back to PATH, so the Ubuntu images keep the exact path they resolved to before this PR.

b5c621e — doc nits

:17-alpine added to the quickstart's "For Existing Deployments" block and to the release-notes body; the pinned CloudSync version in the Dockerfile usage example replaced with a placeholder.

1a3e635 — CI smoke test before publish

Each matrix row now builds amd64 natively, loads it, boots it and runs CREATE EXTENSION cloudsync; SELECT cloudsync_version(); before the multi-arch push, so a broken artifact cannot reach Docker Hub.

Measured cost on run 29998811353 — all five rows green:

Row smoke build smoke test push job total before
supabase pg17-alpine 47 s 2 s 37 s 118 s 92 s
supabase pg17 (Ubuntu) 151 s 3 s 71 s 264 s 122 s
supabase pg15 (Ubuntu) 126 s 3 s 55 s 223 s ~114–146 s
postgres pg17 19 s 3 s 132 s 173 s 175 s
postgres pg15 19 s 2 s 135 s 178 s 183 s

Workflow wall clock: 778 s vs 738 s (+40 s)docker-publish is not on the critical path. The push step dropped 17–22 s on every row, confirming the amd64 layers are reused from the builder cache; the remaining cost is the load export, which scales with image size (hence the large Ubuntu Supabase rows).

Open points now settled

  • Point 3 (amd64 not tested) — resolved. The Alpine row's log shows CREATE EXTENSION1.1.2 on amd64, one second before the push begins. The real 17.6.1.151 base was additionally verified by hand on arm64 (full container boot + CREATE EXTENSION).
  • Point 1 (:17 / :15 alias scheme) — stays deferred, now tracked in Ship :17-ubuntu before flipping the :17 Supabase alias to Alpine #59. Recommendation there: do not flip :17 until the Alpine image has been exercised inside a full Supabase stack, and ship :17-ubuntu one release ahead of the flip.

🤖 Generated with Claude Code

Make the published sqlite-sync-supabase images build on Supabase's newer
Alpine-userland Postgres bases alongside the existing Ubuntu ones. Despite the
Alpine userland these images run a glibc Nix postgres, so the same glibc
extension artifact is installed - only the build-time tooling (apt-get vs apk)
and the pg_config path differ.

- Dockerfile.supabase.release: detect apk/apt-get and resolve pg_config
  dynamically; one file now serves both base families
- main.yml: add PG17 Alpine publish row (17.6.1.151) with a -alpine tag suffix
  so it does not collide with the :17/:15 aliases; bump PG15 base pin
  15.8.1.085 -> 15.8.1.135
- docs: document the Ubuntu vs Alpine base split and refresh illustrative
  PG15 base tags
@damlayildiz
damlayildiz marked this pull request as draft July 22, 2026 16:01
@damlayildiz
damlayildiz requested a review from andinux July 22, 2026 17:03
@damlayildiz
damlayildiz marked this pull request as ready for review July 22, 2026 17:03
andinux and others added 3 commits July 23, 2026 10:51
- workflow: include tag_suffix in the docker-publish job name, so the
  Ubuntu and Alpine PG17 rows no longer render as the same check name
- only remove curl again when it was not already in the base image, and
  skip installing it entirely when a beta artifact is staged; drop the
  GNU tar install (busybox tar extracts the release tarball)
- CLOUDSYNC_PG_CONFIG is an ARG, so --build-arg actually overrides it and
  the empty value no longer leaks into the published image
- fail with an actionable message when CLOUDSYNC_VERSION is not passed
- resolve pg_config from the Nix profiles the Supabase bases ship before
  falling back to PATH, keeping the Ubuntu images on their previous path

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Also drop the pinned CloudSync version from the Dockerfile usage example
so it does not go stale.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Build amd64 natively and load it, then boot the image and run
CREATE EXTENSION cloudsync / cloudsync_version() against it. The
multi-arch push only runs once that passes, so a broken artifact
cannot reach Docker Hub, and amd64 stops being the untested arch.

Readiness is probed over TCP because the entrypoint's init-time server
listens on the unix socket only.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
andinux and others added 2 commits July 23, 2026 18:44
Single-Machine Docker-in-Docker image: dockerd plus a pinned copy of the
upstream Supabase compose tree, with our overrides layered via COMPOSE_FILE
and all state on a Fly volume at /data.

No deployment-specific identifier is committed: the app name, region and org
are passed to `fly deploy`, the public URLs are derived from FLY_APP_NAME at
boot, and the image tag defaults to the released :17-alpine with CLOUDSYNC_IMAGE
as the override for beta builds.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- layer docker-compose.envoy.yml so the stack runs Envoy (api-gw) instead of
  Kong, ahead of upstream making it the default in the ~Aug 2026 release
- keep a failed `compose up` from crash-looping the Machine into Fly's restart
  cap, and bound the Postgres wait so post-init cannot hang
- remove containers whose service left the compose config before starting:
  --remove-orphans ignores profile-disabled services, and the stale
  supabase-kong container held port 8000 so Envoy could not bind
- document the deployment on its own and drop the superseded manual VM guide
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants