Owly Post
Self-hosted

Install with Docker Compose

From compose file to a running stack, with a healthcheck to confirm it works

This is the full install. Every step is a command; what a setting means is on Configuration.

Clone the repository

Terminal
git clone https://github.com/esmeepeters/owlypost.git
cd owlypost
cp .env.example .env

The compose file pulls the prebuilt image from GHCR; no local build is needed.

Set a database password

The stack refuses to start while POSTGRES_PASSWORD is empty, so a well-known default can never sneak into production. Generate one and put it in .env:

Terminal
openssl rand -base64 24
.env
POSTGRES_PASSWORD=paste-the-generated-value

The bundled Postgres reads it, and the compose file wires it into DATABASE_URL for the containers. Using an external Postgres instead? See the last section on this page.

Add an LLM key

Pick one provider. For Anthropic (the default) add your API key:

.env
ANTHROPIC_API_KEY=sk-ant-...

For OpenAI, or for a local Ollama, see LLM providers. Everything else in .env has a working default.

Start the stack

Terminal
docker compose up -d

This starts four services in order: postgres, then a one-shot migrate service that applies the database schema and exits, then app and worker. To pin a version instead of latest, set OWLYPOST_TAG=1.1.0 in .env first.

Check that it works

There is no dedicated health endpoint; use the three checks below.

Terminal
docker compose ps

Expected: postgres is healthy, migrate has exited with code 0, app and worker are running.

Terminal
docker compose logs worker

Expected, among the first lines:

Database schema is up to date.
Scheduler started (timezone UTC): ingest "0 */6 * * *", cleanup "15 4 * * *".
Digest scheduled weekly on Sunday at 17:00 ("0 17 * * 0", UTC).
Terminal
curl -sI http://localhost:3000 | head -1

Expected: HTTP/1.1 200 OK. Now open http://localhost:3000.

Add a source and generate a digest

On Sources, paste any URL (a newsletter, blog, YouTube channel or podcast) and press Detect feed, then Add source. Press Fetch now to collect items right away. On Digests, press Generate digest now.

When the first digest arrives

The digest is scheduled weekly on Sunday at 17:00 by default; change that under Settings. A manual run is fire-and-forget: the button says it started, and the digest appears on the Digests page a minute or two later. If it does not, see Troubleshooting.

Optional: seed example sources

Terminal
docker compose exec app node scripts/seed.ts

Optional: run from source

Build the image locally under a tag and point compose at it:

Terminal
docker build -t ghcr.io/esmeepeters/owlypost:dev .
OWLYPOST_TAG=dev docker compose up -d

Using an external Postgres

Point DATABASE_URL in .env at your database (a managed provider or a Supabase project's Postgres connection string works; add ?sslmode=require for managed providers). Then remove the postgres service and the DATABASE_URL override from docker-compose.yml. Migrations are idempotent and safe to run against an existing database. One migration needs the btree_gist extension, which requires superuser rights on some managed providers.

Next

On this page