Self-host Quickstart
Get Notipo running with Docker Compose, Traefik, and Let's Encrypt TLS in about five minutes.
Prerequisites
Before you start, make sure you have:
- A Linux VPS with Docker Engine 24+ and the Compose plugin installed (1 vCPU / 1 GB RAM is enough for personal use)
- A domain name with an A record pointing at the server's public IP
- Ports 80 and 443 open to the internet (Let's Encrypt verifies via HTTP-01 on port 80, then traffic moves to 443)
- A WordPress site with Application Passwords enabled (WP 5.6+)
- Optional: a Resend account if you want email verification, password-reset emails, or admin signup notifications. Without it, signups are auto-verified on creation.
Clone the repo
The compose stack lives in the app repo. Clone it onto your server:
git clone https://github.com/kfuras/notipo-app.git
cd notipo-appYou're only going to use docker-compose.yml and the apps/api/.env.example file from this repo. The Docker images themselves are pulled from GHCR — no local build needed.
Create the .env file
Copy the example file to the repo root and fill in the values:
cp apps/api/.env.example .envRequired variables:
# Domain that points at this server's public IP (no protocol).
DOMAIN=notipo.example.com
# Email used by Let's Encrypt for cert expiry notifications.
ACME_EMAIL=admin@example.com
# Postgres password for the bundled database container.
DB_PASSWORD=$(openssl rand -hex 24)
# Required by Notipo to wire DATABASE_URL inside containers.
DATABASE_URL=postgresql://notipo:${DB_PASSWORD}@postgres:5432/notipo
# 64-char hex key — encrypts WordPress and Notion credentials at rest.
# Generate ONCE and back up safely. Rotating this loses access to all
# stored credentials.
ENCRYPTION_KEY=$(openssl rand -hex 32)
# Admin API key for the /api/admin/* routes. Long random string.
API_KEY=$(openssl rand -hex 32)
# Allow new users to sign up. Set to false to lock down to existing users only.
ALLOW_SIGNUP=true
# Optional: Resend account for transactional email. If unset, signups
# auto-verify on creation and password-reset emails are unavailable.
# RESEND_API_KEY=re_...
# RESEND_FROM_EMAIL=noreply@example.comSee Configuration for the full env reference, including optional features (Notion OAuth, Gemini AI images, Stripe billing, custom branding for the email templates).
Start the stack
One command pulls the images, runs Postgres, applies database migrations on first boot, and starts the API and admin UI behind Traefik with a Let's Encrypt certificate:
docker compose up -dTail the logs to watch the cert request and the first migration run:
docker compose logs -fOnce you see Server listening on 0.0.0.0:3000 from the API container and Traefik reports the cert is issued, the stack is ready.
Create the first account
Open https://your-domainin a browser. You'll be redirected to the signup page. Register with your email and password — without Resend configured, the account is auto-verified on creation and you're logged in immediately.
Self-host mode (no STRIPE_SECRET_KEY set) skips the trial and lands new accounts on the Pro plan automatically. All features unlocked, no usage limits.
To lock the instance to existing users only after you've created your account, set ALLOW_SIGNUP=false in .env and run docker compose up -d again.
Connect WordPress
From the dashboard, enter your WordPress site URL and click Connect WordPress. WordPress redirects you to its application-password approval page; one click and you're back in Notipo with the credentials saved (encrypted with your ENCRYPTION_KEY).
success_url parameter (with a localhost / 127.0.0.1 exception for dev). The compose stack handles this automatically via Traefik + Let's Encrypt — if you put Notipo behind a TLS-terminating proxy you control yourself, make sure the public URL ends up as https://. If you can't (yet), use the Enter credentials manually link to paste your username + application password directly.Open the Write page and publish your first post end-to-end as a quick smoke test.
Common issues
Let's Encrypt rate-limit during repeated startup tests
If you restart the stack many times during setup, Let's Encrypt will rate-limit cert issuance for ~1 hour. Use the staging environment while testing by editing the Traefik command in docker-compose.yml to add --certificatesresolvers.letsencrypt.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory, then remove it once you're ready for the real cert.
Resend emails not arriving (only if Resend is configured)
Verify your RESEND_FROM_EMAIL domain inside the Resend dashboard. Until DNS records (SPF, DKIM, DMARC) are verified, Resend silently drops messages. Check the Resend logs first — if you don't see the message, the API request never landed; check docker compose logs appfor an error. (If you don't need transactional email, leave Resend unset — signups auto-verify in self-host mode.)
One-click WordPress flow fails or doesn't redirect back
Most likely one of two things:
- Notipo is not on HTTPS. WordPress refuses non-HTTPS
success_urlvalues (localhost is the only exception). Put TLS in front of Notipo before retrying. - WordPress can't reach Notipo from its loopback IP. Some hosts block outbound from the WP server — the inline credentials test from the Notipo container can't complete the round-trip.
In both cases, click Enter credentials manually and paste your WordPress username + application password directly. The manual fallback works without HTTPS or loopback connectivity.
ARM hosts (Apple Silicon, Raspberry Pi)
The GHCR images are multi-arch (linux/amd64 + linux/arm64), so ARM works out of the box — Docker pulls the right variant automatically.
Related
Continue reading