DirectorySecurity AdvisoriesPricing
Sign in
Directory
supabase-postgres logo

supabase-postgres

packaged by Chainguard

Last changed
Request a free trial

Contact our team to test out this image for free. Please also indicate any other images you would like to evaluate.

Tags
Overview
Comparison
Provenance
Specifications
SBOM
Vulnerabilities
Advisories

Chainguard Container for supabase-postgres

Unmodified Postgres with some useful extensions

Chainguard Containers are regularly-updated, secure-by-default container images.

Download this Container Image

For those with access, this container image is available on cgr.dev:

docker pull cgr.dev/ORGANIZATION/supabase-postgres:latest

Be sure to replace the ORGANIZATION placeholder with the name used for your organization's private repository within the Chainguard Registry.

Compatibility Notes

Supabase Postgres is unmodified PostgreSQL bundled with the extensions and configuration that the Supabase platform relies on, such as supautils, pgsodium, Supabase Vault, pg_cron, pg_net, pgaudit, and TimescaleDB. It is licensed under the PostgreSQL License. This image follows the upstream PostgreSQL 15 stream (Dockerfile-15).

Chainguard's supabase-postgres image is comparable to the upstream supabase/postgres image on Docker Hub, with these differences:

  • PostgreSQL and its extensions come from Chainguard packages instead of a Nix store. Binaries are on the default PATH under /usr/bin, so the upstream Nix PATH entry, LOCALE_ARCHIVE, and GRN_PLUGINS_DIR are not set.
  • These upstream extensions are not included yet: pg_jsonschema, wrappers, plv8, pgroonga, pgrouting, and postgis_sfcgal. The WAL-G binary is not included either. A database that has one of these extensions installed fails when it uses it, with could not access file "$libdir/...".
  • Image tags follow the Supabase release that the configuration and migrations come from. PostgreSQL comes from Chainguard's postgresql-15 package, so its minor version can be newer than the one in the tag. postgres --version shows it.
  • The server listens on both the /var/run/postgresql and /tmp Unix sockets.
  • The entrypoint drops privileges with gosu instead of su-exec.
  • The image has no HEALTHCHECK and no EXPOSE entry. Publish port 5432 with -p, and add a health check where you run the container, such as the upstream pg_isready -U postgres -h localhost.

The image keeps the upstream entrypoint (docker-entrypoint.sh), command (postgres -D /etc/postgresql), stop signal (SIGINT), and the Supabase configuration files under /etc/postgresql and /etc/postgresql-custom. Supabase's schema migrations in /docker-entrypoint-initdb.d run the first time the container initializes an empty data directory.

Like upstream, the container starts as root. The entrypoint fixes the ownership of the data directory and then runs PostgreSQL as the postgres user (UID 100, GID 101).

Prerequisites

The pgsodium and Supabase Vault root key is generated on first start and written to /etc/postgresql-custom/pgsodium_root.key. Vault secrets can only be decrypted with that key, so keep /etc/postgresql-custom on a volume next to the data directory, as Supabase's own Docker Compose file and Helm chart do. A container without that volume loses the key, and every Vault secret, when it is recreated.

Both volumes must be writable by UID 100 and GID 101. The entrypoint changes the ownership of the data directory itself. A new, empty Docker volume at /etc/postgresql-custom gets the image's files and ownership.

Getting Started

Start a database with persistent data and configuration volumes. POSTGRES_PASSWORD sets the password of the supabase_admin superuser:

docker run -d --name supabase-db -p 127.0.0.1:5432:5432 \
  -e POSTGRES_PASSWORD=change-me \
  -v supabase-db-data:/var/lib/postgresql/data \
  -v supabase-db-config:/etc/postgresql-custom \
  cgr.dev/ORGANIZATION/supabase-postgres:latest

Wait for the server to finish initializing, then connect:

docker exec supabase-db pg_isready -U postgres -h localhost
docker exec -it supabase-db psql -h /var/run/postgresql -U supabase_admin -d postgres

List the installed and available extensions:

docker exec supabase-db psql -h /var/run/postgresql -U supabase_admin -d postgres \
  -c 'SELECT name, installed_version FROM pg_available_extensions ORDER BY name'

Helm

The community Supabase chart deploys this image as its db component, and keeps /etc/postgresql-custom on its pgsodium volume. Its image.db values select the database image.

Add the chart repository:

helm repo add supabase https://supabase-community.github.io/supabase-kubernetes
helm repo update

Create a values file that points the db component at the Chainguard image:

cat > values.yaml <<EOF
image:
  db:
    repository: cgr.dev/ORGANIZATION/supabase-postgres
    tag: latest
EOF

Install the chart with the values file:

helm install supabase supabase/supabase \
  --namespace supabase --create-namespace \
  --values values.yaml

The chart's default database image is a PostgreSQL 17 build of supabase/postgres. This image is PostgreSQL 15, so use it for new releases, or for releases whose database already runs a 15.x image. A PostgreSQL 15 server cannot start on a data volume that PostgreSQL 17 initialized.

If you pull from a private Chainguard registry, create an image pull secret in the namespace and reference it with image.db.pullSecrets in values.yaml. Refer to the chart's values.yaml for the database password and other settings.

Reusing data from the upstream image

You can start this image on data and configuration volumes that the upstream image created. Stop the upstream container first, and back up both volumes.

  1. Before the first start, add the configuration files the volume lacks, replace its extension scripts with this image's, and give it to UID 100. Upstream 15.8.x images have no conf.d directory, which the current postgresql.conf includes, and ran PostgreSQL as UID 105. The scripts in extension-custom-scripts run when an extension is created, and this image's copies carry its compatibility fixes, such as for postgis_tiger_geocoder and pgmq. Other existing files, including pgsodium_root.key, are kept:

    docker run --rm --user 0 --entrypoint sh \
      -v supabase-db-config:/mnt/config \
      cgr.dev/ORGANIZATION/supabase-postgres:latest -c '
        cd /etc/postgresql-custom
        find . -mindepth 1 | while read -r p; do [ -e "/mnt/config/$p" ] || cp -a "$p" "/mnt/config/$p"; done
        rm -rf /mnt/config/extension-custom-scripts
        cp -a extension-custom-scripts /mnt/config/
        chown -R 100:101 /mnt/config'
  2. Start the container with both volumes, as in Getting Started.

  3. Bring every database up to this image. This image ships only the current TimescaleDB library, so in a database that has TimescaleDB, other queries fail until ALTER EXTENSION timescaledb UPDATE has run as the first command of a session. Other extensions are also behind this image's versions, and this image's glibc is newer than upstream's, which changes collation versions. Run this from the host for the supabase-db container:

    (
      set -eo pipefail
      psql_db() {
        docker exec -i -e PGOPTIONS='-c search_path=public,extensions' supabase-db \
          psql -X -q -v ON_ERROR_STOP=1 -h /var/run/postgresql -U supabase_admin -d "$@"
      }
      dbs=$(psql_db template1 -tAc 'SELECT datname FROM pg_database WHERE datallowconn ORDER BY 1')
      for db in $dbs; do
        # TimescaleDB first, as the first command of its own session.
        if ! out=$(psql_db "$db" -c 'ALTER EXTENSION timescaledb UPDATE' 2>&1); then
          case $out in *'does not exist'*) ;; *) echo "$db: $out" >&2; exit 1 ;; esac
        fi
        # Every other extension that is behind this image's version. PostGIS is updated last.
        exts=$(psql_db "$db" -tAc "SELECT e.extname FROM pg_extension e
            JOIN pg_available_extensions a ON a.name = e.extname
            WHERE e.extversion <> a.default_version AND e.extname NOT LIKE 'postgis%' ORDER BY 1")
        for ext in $exts; do
          psql_db "$db" -c "ALTER EXTENSION \"$ext\" UPDATE"
        done
        # Rebuild indexes. The new collation versions are recorded only after the rebuild succeeds.
        psql_db "$db" -c "REINDEX DATABASE \"$db\""
        psql_db "$db" -c "ALTER DATABASE \"$db\" REFRESH COLLATION VERSION"
        psql_db "$db" -tAc "SELECT format('ALTER COLLATION %I.%I REFRESH VERSION;', n.nspname, c.collname)
            FROM pg_collation c JOIN pg_namespace n ON n.oid = c.collnamespace
            WHERE c.collencoding IN (-1, pg_char_to_encoding(getdatabaseencoding()))
              AND c.collversion <> pg_collation_actual_version(c.oid)" | psql_db "$db"
        postgis=$(psql_db "$db" -tAc "SELECT 1 FROM pg_extension WHERE extname = 'postgis'")
        if [ -n "$postgis" ]; then
          psql_db "$db" -c 'SELECT postgis_extensions_upgrade()'
        fi
      done
    )

    For each database, it updates TimescaleDB first, then every other extension that is behind, rebuilds the indexes and records the new collation versions, and updates PostGIS with postgis_extensions_upgrade(). The script stops at the first command that fails, so a database whose indexes did not rebuild keeps its old collation versions and its collation warnings. Fix the cause and run the script again.

Databases that use an extension that this image does not include yet keep failing on that extension.

Configuration

The image sets these environment variables, matching upstream:

  • PGDATA: The data directory, /var/lib/postgresql/data.
  • POSTGRES_USER: The superuser created at initialization, supabase_admin.
  • POSTGRES_DB: The default database, postgres.
  • POSTGRES_HOST: The Unix socket directory the migrations connect through, /var/run/postgresql.
  • LANG, LANGUAGE, LC_ALL: en_US.UTF-8.
  • PATH: Starts with /usr/lib/postgresql/bin, which holds pgsodium_getkey.sh.

Set POSTGRES_PASSWORD when you start the container. The other variables of the docker-library entrypoint, such as POSTGRES_INITDB_ARGS, also work.

The server reads /etc/postgresql/postgresql.conf, which includes the files in /etc/postgresql-custom/conf.d. To change a setting, add a .conf file to that directory on your configuration volume and restart the container.

Documentation and Resources

What are Chainguard Containers?

Chainguard's free tier of Starter container images are built with Wolfi, our minimal Linux undistro.

All other Chainguard Containers are built with Chainguard OS, Chainguard's minimal Linux operating system designed to produce container images that meet the requirements of a more secure software supply chain.

The main features of Chainguard Containers include:

For cases where you need container images with shells and package managers to build or debug, most Chainguard Containers come paired with a development, or -dev, variant.

In all other cases, including Chainguard Containers tagged as :latest or with a specific version number, the container images include only an open-source application and its runtime dependencies. These minimal container images typically do not contain a shell or package manager.

Although the -dev container image variants have similar security features as their more minimal versions, they include additional software that is typically not necessary in production environments. We recommend using multi-stage builds to copy artifacts from the -dev variant into a more minimal production image.

Need additional packages?

To improve security, Chainguard Containers include only essential dependencies. Need more packages? Chainguard customers can use Custom Assembly to add packages, either through the Console, chainctl, or API.

To use Custom Assembly in the Chainguard Console: navigate to the image you'd like to customize in your Organization's list of images, and click on the Customize image button at the top of the page.

Learn More

Refer to our Chainguard Containers documentation on Chainguard Academy. Chainguard also offers VMs and Libraries — contact us for access.

Trademarks

This software listing is packaged by Chainguard. The trademarks set forth in this offering are owned by their respective companies, and use of them does not imply any affiliation, sponsorship, or endorsement by such companies.

Licenses

Chainguard's container images contain software packages that are direct or transitive dependencies. The following licenses were found in the "latest" tag of this image:

  • BSD-2-Clause

  • BSD-3-Clause

  • GPL-3.0-or-later

  • LGPL-2.1

  • MIT

  • Qhull

  • Unicode-TOU

For a complete list of licenses, please refer to this Image's SBOM.

Software license agreement

Compliance

Chainguard Containers are SLSA Level 3 compliant with detailed metadata and documentation about how it was built. We generate build provenance and a Software Bill of Materials (SBOM) for each release, with complete visibility into the software supply chain.

SLSA compliance at Chainguard

This image helps reduce time and effort in establishing PCI DSS 4.0 compliance with low-to-no CVEs.

PCI DSS at Chainguard

Category
Application

The trusted source for open source

Talk to an expert
PrivacyTerms

Product

Chainguard ContainersChainguard LibrariesChainguard VMsChainguard OS PackagesChainguard ActionsChainguard Agent SkillsIntegrationsPricing
© 2026 Chainguard, Inc. All Rights Reserved.
Chainguard® and the Chainguard logo are registered trademarks of Chainguard, Inc. in the United States and/or other countries.
The other respective trademarks mentioned on this page are owned by the respective companies and use of them does not imply any affiliation or endorsement.