Database

PostgreSQL job data feed

Job listings in real tables, queryable with plain SQL.

Any PostgreSQL instance works the same way — managed RDS, Cloud SQL, Neon, or a server you run yourself — because Jobo speaks the standard Postgres wire protocol over TLS. It creates the target table on first sync and upserts on job ID, so a re-push updates rows in place instead of appending duplicates, and the incremental run every 15 minutes only touches jobs that actually changed. The table has a fixed relational shape, which is why transformation pipelines are not offered here: use MongoDB or Elasticsearch if you need arbitrary document structures.

Sync
Incremental, every 15 min
Deduplication
Upsert on job ID
Pipelines
Not supported
Credentials
AES-256 at rest

Connection configuration

The fields Jobo collects to reach your PostgreSQL instance.

hoststringrequired

Database server hostname or IP address.

portnumberrequired

Connection port. Defaults to 5432.

databasestringrequired

Target database name.

schemastring

Target schema. Defaults to "public".

tablestring

Target table, created automatically. Defaults to "jobs".

usernamestringrequired

Database user with write access.

passwordstringrequired

User password. Encrypted at rest with AES-256.

ssl_modeselect

SSL mode: disable, allow, prefer, require, verify-ca, verify-full.

delete_expired_jobsboolean

Delete expired jobs (default). Disable to keep them with an expired_at timestamp.

PostgreSQL config
{
  "host": "db.example.com",
  "port": 5432,
  "database": "jobs_db",
  "schema": "public",
  "table": "jobs",
  "username": "jobo_sync",
  "password": "••••••••",
  "ssl_mode": "require",
  "delete_expired_jobs": true
}

How to sync jobs to PostgreSQL

One-time setup. After this, the feed maintains itself.

  1. 1

    Create a least-privilege role for the feed

    The feed needs CONNECT on the database plus USAGE and CREATE on one schema — CREATE because it builds the table itself on first sync. It then owns that table, so no further table grants are needed. Do not reuse your application role and never point the feed at a superuser.

    sql
    CREATE USER jobo_sync WITH PASSWORD 'your_secure_password';
    GRANT CONNECT ON DATABASE jobs_db TO jobo_sync;
    GRANT USAGE, CREATE ON SCHEMA public TO jobo_sync;
  2. 2

    Open the network path

    Postgres only accepts what you explicitly allow. Confirm listen_addresses covers the interface Jobo reaches, add a pg_hba.conf line for the feed user and Jobo's IP range, reload the configuration, and open the port in your firewall or cloud security group. Contact support for the current egress range.

    bash
    # pg_hba.conf — TLS-only access for the feed role
    hostssl  jobs_db  jobo_sync  <jobo-ip>/32  scram-sha-256
    
    # apply without a restart
    sudo -u postgres psql -c 'SELECT pg_reload_conf();'
  3. 3

    Point the feed at a dedicated table

    Set schema and table to something nothing else writes to, such as public.jobs. The schema must already exist — Jobo creates tables, not schemas. Force resync drops and recreates the configured table, so sharing it with application data will lose that data. Keep ssl_mode at require or higher on anything reachable over the internet.

  4. 4

    Activate and query it

    The table and its indexes appear on the first push, and every run after that upserts on job ID. With delete_expired_jobs left on, expired listings are removed outright; turn it off and they stay with an expired_at timestamp you can filter on.

    sql
    SELECT title, company, locations, apply_url
    FROM public.jobs
    WHERE expired_at IS NULL
    ORDER BY date_posted DESC
    LIMIT 20;

Common issues

What actually goes wrong when connecting PostgreSQL, and the fix.

high

The connection test times out or is refused.

Work outwards: firewall or security group first, then listen_addresses, then a matching pg_hba.conf line for the feed user and source IP. A refusal usually means Postgres is not listening on that interface; a timeout usually means the packet never arrived.

high

The push fails with "permission denied for schema public".

The role can connect but cannot create the table. Grant USAGE and CREATE on the target schema. On PostgreSQL 15 and newer the public schema no longer grants CREATE to everyone, so this bites upgraded clusters in particular.

medium

The push fails because the target schema does not exist.

Automatic creation covers the table only. Run CREATE SCHEMA yourself and grant the feed role USAGE and CREATE on it, or leave the field empty to use public.

medium

Rows the app wrote to the jobs table disappeared.

Force resync drops and recreates the configured table by design. Give the feed a table of its own and have your application read from it rather than write to it.

Best practices

  • Give the feed its own table — force resync drops and recreates whatever it points at.
  • Grant the role CONNECT plus USAGE and CREATE on one schema, and nothing more.
  • Keep ssl_mode at require or higher on any instance reachable over the internet.
  • Add your own indexes for the columns you filter and sort on; the feed only creates its own.
  • Narrow the feed with location and source filters instead of syncing the whole catalogue.

Start syncing jobs to PostgreSQL

Connect the destination, pick your filters, and let the feed run.