Skip to content

pgs3

S3-compatible object storage endpoint implemented inside PostgreSQL

Overview

PackageVersionCategoryLicenseLanguage
pgs30.1.1SIMApache-2.0Rust
IDExtensionBinLibLoadCreateTrustRelocSchema
9430pgs3NoYesYesYesNoNopgs3

Early alpha; PG17-18; endpoint startup requires preload or pgs3.start(); TLS terminates externally; small-object and 100,000-object Fork performance gates remain unmet.

Version

TypeRepoVersionPG VerPackageDeps
EXTPIGSTY0.1.11817161514pgs3-
RPMPIGSTY0.1.11817161514pgs3_$v-
DEBPIGSTY0.1.11817161514postgresql-$v-pgs3-
OS / PGPG18PG17PG16PG15PG14
el8.x86_64N/AN/AN/A
el8.aarch64N/AN/AN/A
el9.x86_64N/AN/AN/A
el9.aarch64N/AN/AN/A
el10.x86_64N/AN/AN/A
el10.aarch64N/AN/AN/A
d12.x86_64N/AN/AN/A
d12.aarch64N/AN/AN/A
d13.x86_64N/AN/AN/A
d13.aarch64N/AN/AN/A
u22.x86_64N/AN/AN/A
u22.aarch64
PIGSTY 0.1.1
PIGSTY 0.1.1
N/AN/AN/A
u24.x86_64N/AN/AN/A
u24.aarch64
PIGSTY 0.1.1
PIGSTY 0.1.1
N/AN/AN/A
u26.x86_64N/AN/AN/A
u26.aarch64N/AN/AN/A

Build

You can build the RPM / DEB packages for pgs3 using pig build:

pig build pkg pgs3         # build RPM / DEB packages

Install

You can install pgs3 directly. First, make sure the PGDG and PIGSTY repositories are added and enabled:

pig repo add pgsql -u          # Add repo and update cache

Install the extension using pig or apt/yum/dnf:

Install
pig install pgs3;          # Install for current active PG version
pig
pig ext install -y pgs3 -v 18  # PG 18
pig ext install -y pgs3 -v 17  # PG 17
dnf
dnf install -y pgs3_18       # PG 18
dnf install -y pgs3_17       # PG 17
apt
apt install -y postgresql-18-pgs3   # PG 18
apt install -y postgresql-17-pgs3   # PG 17

Preload:

shared_preload_libraries = 'pgs3';

Create Extension:

CREATE EXTENSION pgs3;

Usage

Sources:

pgs3 0.1.1 turns one PostgreSQL database into a path-style S3-compatible endpoint. PostgreSQL background workers authenticate SigV4 requests and execute versioned object operations against ordinary SQL tables, so object metadata, payloads, authorization, WAL, physical backup, and recovery remain inside PostgreSQL. It targets PostgreSQL 17 and 18 and remains early-alpha software.

Core Workflow

Install the extension in the database that will own the object store, create a restricted tenant role and credential, then start the worker pool:

CREATE EXTENSION pgs3;

CREATE ROLE tenant_app
  NOLOGIN NOINHERIT NOSUPERUSER NOCREATEDB NOCREATEROLE
  NOREPLICATION NOBYPASSRLS;

SELECT pgs3.create_credential(
  'tenant-access-key', 'replace-with-a-secret', 'tenant_app'::name, true
);

SELECT pgs3.start();
TABLE pgs3.worker_state;
TABLE pgs3.stats;

For automatic startup, preload the library and configure the target database before restarting PostgreSQL:

shared_preload_libraries = 'pgs3'
pgs3.enabled = on
pgs3.target_database = 'artifacts'
pgs3.listen_addr = '127.0.0.1'
pgs3.port = 9000
pgs3.workers = 4

Adding or removing shared_preload_libraries, or changing pgs3.target_database, requires a PostgreSQL restart. Other documented settings use SIGHUP semantics, but operational readiness must be checked through pgs3.worker_state and logs rather than only an open TCP port. A manually started pool can be stopped with pgs3.stop().

Client and Storage Behavior

Clients must use path-style addressing and an explicit endpoint:

export PGS3_ENDPOINT='https://s3.example.com'
export AWS_ACCESS_KEY_ID='<access-key>'
export AWS_SECRET_ACCESS_KEY='<secret-key>'
export AWS_DEFAULT_REGION='us-east-1'

aws --endpoint-url "$PGS3_ENDPOINT" s3api list-buckets

Always pass --endpoint-url; otherwise an AWS client can silently send a request to AWS. The supported client paths include AWS CLI, boto3, rclone, s3fs, and DuckDB httpfs. Bucket and object operations include range and conditional reads/writes, ListObjectsV2, permanent version history, delete markers, CopyObject, and multipart upload.

The canonical payload lives in pgs3.blob. Object versions, CopyObject, SQL Restore, and metadata-only Fork operations can share the same blob rather than duplicating bytes. One endpoint serves one configured database.

Operations and Security

Credential access keys map to PostgreSQL tenant roles. Keep those roles NOLOGIN, NOINHERIT, and NOBYPASSRLS; do not use pgs3.server_role as an application identity. Row-level security is the tenant-isolation boundary, while credential-management and worker-control functions remain operator-only.

SigV4 requires reversibly stored secrets, so database backups and replicas contain credential material and must be encrypted and access-controlled. pgs3 serves cleartext HTTP; production deployments need an external TLS proxy that preserves the signed path, host, and headers. Use physical backups: logical dump/restore is not supported for the extension-owned object state.

Compatibility and Limitations

The packaged and upstream-supported paths are PostgreSQL 17 and 18 with pgrx 0.19.2. Version 0.1.1 includes the tested 0.1.0 -> 0.1.1 extension upgrade edge.

pgs3 is not a general-purpose production S3 replacement. Virtual-host addressing, built-in TLS, IAM or bucket-policy languages, ACLs, lifecycle rules, and cross-database routing are not implemented. Small-object GET/PUT targets and the 100,000-object Fork target are not met, and full-object GET currently materializes the response in memory. Set a deployment-specific object-size limit and review the upstream limitations before exposing an endpoint.

Was this page helpful?