Quickstart: PostgreSQL
In about ten minutes you will load public sample JSON from S3 into a PostgreSQL table and query it with psql. You can run PostgreSQL locally in Docker, so you do not need a cloud warehouse account.
Skipprd infers the schema, creates the PostgreSQL schema and table for you, and inserts the rows.
Before you begin
You need:
- Skipprd installed. Check with
skipprd --version, orpython -c "import skippr"if you use Python. See Install. - PostgreSQL reachable from the machine running Skipprd. This guide starts one with Docker; check with
docker --version. To use an existing server instead, you need a database and a user that can create a schema and tables in it. - AWS credentials that can read
s3://skippr-public-sample-data/bike-hire/. Any AWS account works.
Export your credentials and a password for PostgreSQL, then create an empty working directory. Skipprd reads skippr.yml from the current directory, so run every command from there.
export AWS_ACCESS_KEY_ID="your-access-key-id"
export AWS_SECRET_ACCESS_KEY="your-secret-access-key"
export AWS_DEFAULT_REGION="us-east-1"
export POSTGRES_PASSWORD="choose-a-password"
mkdir skippr-postgres && cd skippr-postgres1. Start PostgreSQL
Skip this step if you already have a server.
docker run --name skippr-postgres -d -p 5432:5432 \
-e POSTGRES_PASSWORD="$POSTGRES_PASSWORD" \
-e POSTGRES_DB=analytics \
postgres:16This starts PostgreSQL on localhost:5432 with a database named analytics and the user postgres.
2. Describe the pipeline
Each tab writes the same skippr.yml.
from skippr import Config, DataSinkPostgres, DataSourceS3, EnvRef, LocalStorage, Pipeline
cfg = Config().workspace("quickstart").storage(LocalStorage())
sample = cfg.data_source(
"sample",
DataSourceS3(s3_bucket="skippr-public-sample-data", s3_prefix="bike-hire"),
)
warehouse = cfg.data_sink(
"warehouse",
DataSinkPostgres(
host="localhost",
port=5432,
user="postgres",
password=EnvRef("POSTGRES_PASSWORD"),
database="analytics",
schema="public",
sslmode="disable",
),
)
cfg.pipeline("bikehire", Pipeline(data_source=sample, data_sink=warehouse))
cfg.save("skippr.yml")skipprd --workspace quickstart --storage-mode local connect data-source s3 \
--pipeline bikehire \
--name sample \
--s3-bucket skippr-public-sample-data \
--s3-prefix bike-hire
skipprd connect data-sink postgres \
--pipeline bikehire \
--name warehouse \
--host localhost \
--port 5432 \
--user postgres \
--password '${POSTGRES_PASSWORD}' \
--database analytics \
--schema public \
--sslmode disableskippr:
workspace: quickstart
skipprd_el_storage_mode: local
pipelines:
bikehire:
data_source: data_sources.sample
data_sink: data_sinks.warehouse
data_sources:
sample:
S3:
s3_bucket: skippr-public-sample-data
s3_prefix: bike-hire
data_sinks:
warehouse:
Postgres:
host: localhost
port: 5432
user: postgres
password: ${POSTGRES_PASSWORD}
database: analytics
schema: public
sslmode: disableWhat the settings mean:
passwordmust be an environment reference. In Python,EnvRef("POSTGRES_PASSWORD")writespassword: ${POSTGRES_PASSWORD}; in the CLI, single quotes stop your shell from expanding it. Skipprd reads the value when the pipeline starts, so the password never lands inskippr.yml. You can also put it in a.envfile next toskippr.yml.sslmode: disablesuits the local Docker container, which has no TLS. For a remote server, leavesslmodeout: the default,prefer, uses TLS when the server offers it.host,port, andschemadefault tolocalhost,5432, andpublic. They are spelled out here so you can see what to change for your own server.LocalStorage()(skipprd_el_storage_mode: local) keeps Skipprd's own state, such as the discovered schema and how far it has read, in./data. That is fine for a trial; for production, see State store.
3. Discover the schema
import skippr
s = skippr.Session(skippr.Config.discover().get_pipeline("bikehire"))
s.discover()skipprd discover --pipeline bikehire
skipprd schema --pipeline bikehirediscover samples the JSON and records each field's name and type. It does not touch PostgreSQL. skipprd schema prints the fields, which become your table's columns.
4. Sync
import skippr
s = skippr.Session(skippr.Config.discover().get_pipeline("bikehire"))
s.sync(once=True)skipprd sync --pipeline bikehire --onceSync reads the source and writes each batch to Skipprd's write-ahead log (WAL) first. A crash retries that committed batch; PostgreSQL applies it once. It then creates the schema and the bikehire table if they do not exist and inserts the rows. When new fields appear later, Skipprd adds the columns before inserting.
--once (once=True) runs a single pass and exits. Without it, sync keeps running and checks the source for new data on a schedule.
Check it worked
Count the rows and look at the table definition:
docker exec -it skippr-postgres psql -U postgres -d analytics \
-c 'SELECT count(*) AS row_count FROM public.bikehire;' \
-c '\d public.bikehire'You should see a non-zero row_count, and columns that match the fields skipprd schema printed in step 3. The table is named after the pipeline. Column names are lower case.
If you use your own server, run the same SELECT with psql or any SQL client.
Troubleshooting
connection refused— PostgreSQL is not listening onhost:port. Check the container is running withdocker ps, or check the host and port of your server.password authentication failed—POSTGRES_PASSWORDin the shell running Skipprd does not match the database user's password. If you changed it after starting the container, recreate the container or update the user's password.database "analytics" does not exist— Skipprd creates schemas and tables, not databases. Create the database first, or setdatabaseto one that exists.permission denied for databaseorfor schema— the user cannot create the schema or tables. GrantCREATEon the database, orUSAGEandCREATEon the schema.- A TLS or SSL error — the server's TLS settings do not match
sslmode. Usesslmode: disablefor a local server without TLS. relation "public.bikehire" does not exist— sync has not written yet, or it failed. Rerunskipprd sync --pipeline bikehire --once --logand look for the error.AccessDeniedreading the sample bucket — your AWS credentials are missing or lacks3:GetObjectands3:ListBucket. Runaws sts get-caller-identityto see which principal Skipprd uses.
For more, see Troubleshooting.
Next steps
- PostgreSQL destination — every option.
- PostgreSQL source — extract from database tables instead of files.
- Change data capture — replicate inserts, updates, and deletes to current state.
- Quickstart: Snowflake — move to a cloud warehouse.
- How Skipprd works — what happens between source and destination.
