SchemaRag supports multiple PostgreSQL databases through server-side profiles. Browser users select a configured profile and its tables; they cannot submit a host, username, password, or connection string.
Run the following as a database administrator and replace the database, schema, role, and password values for your environment:
CREATE ROLE schema_rag_reader
WITH
LOGIN
PASSWORD '<generate-a-strong-password>'
NOSUPERUSER
NOCREATEDB
NOCREATEROLE
NOINHERIT
NOREPLICATION
NOBYPASSRLS
CONNECTION LIMIT 5;
GRANT CONNECT ON DATABASE reporting TO schema_rag_reader;
GRANT USAGE ON SCHEMA public TO schema_rag_reader;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO schema_rag_reader;
ALTER ROLE schema_rag_reader SET default_transaction_read_only = on;
ALTER ROLE schema_rag_reader SET statement_timeout = '5s';
ALTER ROLE schema_rag_reader SET lock_timeout = '1s';
ALTER ROLE schema_rag_reader SET idle_in_transaction_session_timeout = '10s';
ALTER ROLE schema_rag_reader SET search_path = pg_catalog;Grant only the schemas and tables that this application may query. Do not reuse an owner, migration, application-write, superuser, or cloud-admin account.
Future tables are intentionally not granted automatically. If you choose to use
ALTER DEFAULT PRIVILEGES, first confirm which owner creates tables and whether
automatic access is acceptable for your data-governance policy.
Copy the safe Compose template:
cp compose.external-db.example.yaml compose.external-db.yamlAdd these values to .env:
EXTERNAL_DB_DISPLAY_NAME=Reporting database
EXTERNAL_DB_CONNECTION_STRING=Host=host.docker.internal;Port=5432;Database=reporting;Username=schema_rag_reader;Password=<reader-password>;SSL Mode=Prefer;Maximum Pool Size=5;Command Timeout=5;Include Error Detail=false
EXTERNAL_DB_SCHEMA=publiccompose.external-db.yaml and .env are ignored by Git. For a shared or hosted
environment, inject the connection string from a secret manager rather than a
checked-in file.
Use a host reachable from the API container. localhost inside the container
refers to the API container, not the Docker host.
docker compose \
-f compose.yaml \
-f compose.external-db.yaml \
up -d --build apiOpen http://localhost:8080/, select the external profile, and click Discover tables. Select only the tables needed for the demo, then index them.
Reindexing replaces the existing Qdrant documents for that profile. It does not append to the previous selection.
Connect with the reader credentials and verify:
SHOW transaction_read_only;
SELECT has_database_privilege(current_user, current_database(), 'TEMP');
SELECT has_schema_privilege(current_user, 'public', 'CREATE');
SELECT table_schema, table_name, privilege_type
FROM information_schema.role_table_grants
WHERE grantee = current_user
AND privilege_type <> 'SELECT';The first result must be on, schema CREATE must be false, and the final
query must return no rows. TEMP should also be false when the database policy
allows it. PostgreSQL commonly grants temporary-table access through PUBLIC,
so review the impact on other roles before revoking that database-wide grant.
- Prefer TLS with certificate verification for remote databases.
- Restrict network access to the API runtime and approved database host.
- Use curated reporting views when tables contain sensitive columns.
- Rotate credentials and audit grants regularly.
- Authentication and per-source authorization are required before exposing the API beyond a trusted local environment.
- Table selection improves relevance; PostgreSQL permissions remain the security boundary.