Project ROUNDTABLE Docs
Getting started

Environment variables

Configure PostgreSQL, NextAuth, AWS S3, local MinIO, logging, and search tuning

Environment variables are runtime configuration, not source code. The main app reads them on the server; the docs site is a separate repository with no dependency on the application's .env. Never commit .env, access keys, or seeded passwords.

Complete reference

VariableRequired?ExampleUsed for
DATABASE_URLYes for the main apppostgresql://postgres:postgres@localhost:5432/roundtablePrisma PostgreSQL connection.
NEXTAUTH_SECRETYesLong random valueSigns NextAuth JWT/session data.
NEXTAUTH_URLYes when deployedhttp://localhost:3000Canonical callback and redirect URL.
UPLOADS_S3_BUCKETYes for uploadsroundtable-uploadsBucket name for private objects.
AWS_REGIONYes for S3us-gov-west-1 or local regionS3 client region.
AWS_ACCESS_KEY_IDUsuallyLocal or task-role credentialS3 credential-chain input.
AWS_SECRET_ACCESS_KEYUsuallyLocal secretS3 credential-chain input.
AWS_ENDPOINT_URL_S3Local/portable onlyhttp://minio:9000S3-compatible endpoint override.
UPLOADS_S3_SSENo (defaults to AES256)noneServer-side encryption requested on uploads. Unset or blank means AES256; none omits the request (local MinIO); AES256, aws:kms, and aws:kms:dsse are passed through. Matched case-insensitively; any other value throws when an upload is attempted.
MINIO_KMS_SECRET_KEYOnly when MinIO must serve SSE requestsSecret valueGives MinIO a KMS key so it can serve the app's SSE request. Not needed if the app runs with UPLOADS_S3_SSE=none.
MINIO_DOMAINMinIO virtual hostminio.localEnables virtual-host bucket addressing.
LOG_LEVELNo (default info)debug, info, warn, or errorMinimum severity for the structured operational logger (src/lib/logger.ts).
MATCHING_SCORERNo (default deterministic)bedrockSelects Bedrock embedding scoring only when set to bedrock (case-insensitive); unset, blank, or another value keeps deterministic tag-overlap scoring.
BEDROCK_EMBEDDING_MODEL_IDNo (default amazon.titan-embed-text-v2:0)Titan-style model IDBedrock model used when matching is enabled. The response must be JSON with an embedding number array; other response shapes fail and routing falls back to tag scoring. An unset or blank value uses the default.
BEDROCK_SIMILARITY_FLOORNo (default 0.35)Number in [0, 1)Similarity at or below this floor scores zero; the remaining range rescales to 0..1. An unset, blank, or invalid value uses 0.35.
SEARCH_SIMILARITY_THRESHOLDNo (default 0.2)Number strictly between 0 and 1Minimum trigram word similarity for a global-search fuzzy match. Read once at startup; a value outside the range logs a warning and the default is used.
SEARCH_BACKENDNo (default hybrid)hybrid, fts, trigram, or prismaGlobal-search backend: full-text search with trigram fuzzy fallback on zero results (default), full-text search only (boolean/phrase/wildcard operators), pg_trgm fuzzy only, or the case-insensitive substring fallback.

AWS SDK credentials should come from the default credential chain in deployed AWS environments, usually an App Runner instance role. Do not put production credentials in the image.

PostgreSQL

DATABASE_URL is consumed by Prisma's datasource. Verify connectivity with:

psql "$DATABASE_URL" -c 'select 1;'

If psql is not installed, use docker exec -it roundtable-pg psql -U postgres -d roundtable -c 'select 1;'.

Port 5432 is already occupied

Either stop the unrelated PostgreSQL process or map the container to another host port, such as -p 55432:5432, and update DATABASE_URL to use localhost:55432. Do not change the container-side port in the Compose network; other services use the service name and internal port.

NextAuth

NEXTAUTH_SECRET must be stable across restarts in a given environment. Changing it invalidates existing signed sessions. NEXTAUTH_URL must match the browser-visible origin in deployment; a wrong HTTPS URL commonly causes callback failures or redirect loops.

S3 and MinIO

src/lib/s3.ts creates an S3Client with AWS_REGION and the default credential chain. AWS_ENDPOINT_URL_S3 is useful for MinIO because it supplies an S3-compatible endpoint without changing application code. Uploads use keys under uploads/ and request server-side encryption controlled by UPLOADS_S3_SSE: unset or blank defaults to AES256 (production behavior), while the explicit value none (matched case-insensitively) omits the SSE parameter so stores without KMS (local MinIO) accept the upload (a warning is logged).

The portable Compose recipe also requires MinIO-specific behavior:

  1. Either set UPLOADS_S3_SSE=none for the app, or set MINIO_KMS_SECRET_KEY so MinIO can satisfy an SSE request.
  2. Set MINIO_DOMAIN so bucket virtual-host addressing resolves.
  3. Ensure the app can resolve the MinIO hostname used in the endpoint and domain.
  4. Use bucket-scoped credentials generated by the minio-init service.

Without these settings, a submission with a file may create a database request that ultimately fails with an S3 500 even though text-only submissions work.

Why virtual-host addressing matters

S3 clients may address a bucket as a path or as a host such as bucket.minio.local. A local endpoint that resolves only minio:9000 can fail when the SDK constructs a virtual-host URL. MINIO_DOMAIN and local DNS/hosts configuration need to agree with the bucket name. This is a network-resolution problem, not a Prisma migration problem.

Safe local example

DATABASE_URL=postgresql://postgres:postgres@localhost:5432/roundtable
NEXTAUTH_SECRET=replace-with-a-long-random-development-value
NEXTAUTH_URL=http://localhost:3000
UPLOADS_S3_BUCKET=roundtable-uploads
AWS_REGION=us-east-1
AWS_ENDPOINT_URL_S3=http://localhost:9000
AWS_ACCESS_KEY_ID=roundtable-local
AWS_SECRET_ACCESS_KEY=replace-me
# local only: the app stops asking the store to encrypt uploads
UPLOADS_S3_SSE=none
MINIO_DOMAIN=localhost

This is a pattern, not a credential to copy into a shared environment. It takes the unencrypted route so MinIO needs no key; the portable bundle takes the other route, keeping the default AES256 request and a MINIO_KMS_SECRET_KEY in deploy/portable/.env.example for MinIO to serve it.

Troubleshooting

Prisma says it cannot connect

Check that the container is running, port 5432 is not mapped to another process, and the hostname in DATABASE_URL is reachable from where pnpm db:setup runs. Inside Compose, use the service name db, not localhost.

Uploads return 500

Confirm the bucket exists, the endpoint is reachable from the app container, the credentials can put objects, and the encryption request matches the store — MinIO needs MINIO_KMS_SECRET_KEY unless the app runs with UPLOADS_S3_SSE=none — plus MinIO domain settings when virtual-host addressing is used. The upload validator can reject a file with 400; a storage configuration failure is a different class of error and is logged server-side.

Secret changes seem ignored

Restart the Next.js process after changing .env. NextAuth also requires a stable secret; do not expect existing sessions to survive a deliberate secret rotation.

Environment review checklist

  • .env exists at the root and is ignored by Git.
  • DATABASE_URL points to the intended database, not a stale container.
  • NEXTAUTH_SECRET is long, random, and stable for this environment.
  • NEXTAUTH_URL matches the browser origin exactly, including HTTPS in deployment.
  • The upload bucket exists and is private.
  • AWS credentials are supplied by a safe local profile or task role.
  • Local MinIO has virtual-host settings, and either UPLOADS_S3_SSE=none or a KMS key.
  • UPLOADS_S3_SSE is unset, blank, or set to one of the encrypting values (AES256, aws:kms, aws:kms:dsse) in production so uploads stay encrypted.

Troubleshooting matrix

SymptomLikely causeNext check
Port 3000 bind errorMain app port is occupiedInspect lsof, stop stale app, or use a deliberate alternate.
Port 3001 bind errorDocs server is already runningReuse the existing docs server or stop the stale process.
P1001 database errorPostgreSQL stopped/unreachabledocker ps, container logs, and host/port in DATABASE_URL.
Migration cannot applyWrong database or migration statepnpm prisma migrate status; inspect the first SQL error.
Prisma client lacks an enumGenerated client staleRun pnpm prisma generate from root.
Upload returns 500S3/MinIO endpoint, bucket, or SSETest credentials, endpoint DNS, bucket policy, and UPLOADS_S3_SSE/KMS support.
Upload returns 400File or field validationCheck extension, size, magic bytes, and Zod limits.
Redirect loops after loginNEXTAUTH_URL mismatchMatch the public scheme/host/port and restart Next.js.
Notifications absentProfile overlap or setting disabledInspect normalized tags, profile, and NotificationSetting.

Do not respond to every failure by deleting the database. First classify whether the error is process startup, network connectivity, schema generation, request validation, authorization, or storage.