heycapy
[ | ]

self-hosting

[back]

heycapy is open source, so you can run it on your own server for free. it’s one docker container plus caddy for https.

start it

you need a server with docker and a domain pointing at it.

git clone https://github.com/heycapy/heycapy
cd heycapy
cp .env.example .env

fill in .env (more below), put your domain in the Caddyfile, then:

docker compose up -d

caddy gets the https certificate by itself. the database lives in a docker volume.

the settings that matter

everything is in .env.example, with a note on each line. the main ones:

settingwhat it’s for
JWT_SECRET, ENCRYPTION_KEYrequired. make them with openssl rand -base64 32 and openssl rand -hex 32
APP_URLyour public address, like https://capy.example.com
ADMIN_EMAILSwho sees the system tab and gets alerts

leave HOSTED out. that one’s only for app.heycapy.xyz.

email

heycapy sends sign in codes and email reminders. pick one of two ways:

  • resend: put your key in RESEND_API_KEY, and the sender in EMAIL_FROM.
  • your own mail server: fill in SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASS and SMTP_FROM. port 587 is the usual one. 465 is ssl, and heycapy switches that on by itself.

with neither, the sign in code shows on the login screen. fine for trying it on your laptop, not for a real server.

each user can also send their own reminders through their own mail server, in tweaks → notifications → email.

telegram

make a bot with @BotFather and put its token in TELEGRAM_BOT_TOKEN. APP_URL has to be a public https address, because telegram sends messages to your server there. after that, connecting works like in the telegram guide.

ai

AI_PROVIDER, AI_MODEL and AI_API_KEY set the ai for users without their own key. any user can still add their own key in tweaks → ai, see the ai guide. ollama works here too, with OLLAMA_URL.

push and ntfy

push notifications work out of the box over https. ntfy works with ntfy.sh or your own ntfy server, set per user in the app.

keeping it running

  • backups: every night the database is copied to a backups/ folder next to it, keeping the last 7. copy that folder off the server too.
  • health: /api/health for your platform, /api/health/scheduler for an uptime monitor. it answers 503 when reminders are stuck.
  • updates: back up first, then see upgrading below.

upgrading

back up first, then move to the new version:

docker compose stop heycapy
docker compose cp heycapy:/data ./heycapy-backup
git fetch --tags
git checkout $(git describe --tags --abbrev=0 origin/main)
docker compose up -d --build

stopping first matters: while heycapy runs, recent changes sit in a separate heycapy.db-wal file that a copy can miss. the database updates itself on start. what changed is in the changelog.

restoring a backup

every night heycapy saves a copy of the database in backups/. each file is complete on its own. you also need the .env from back then: without the same ENCRYPTION_KEY, the keys and tokens saved in the app can’t be read. a different JWT_SECRET only signs everyone out.

  1. stop heycapy and get the backup file next to your docker-compose.yml. if it’s still on the server:

    docker compose stop heycapy
    docker compose cp heycapy:/data/backups ./heycapy-backups
    

    on a new server, put your off-server copy there instead and set up .env and the Caddyfile as in the start section.

  2. restore it, with the name of the file you want:

    docker compose run --rm --no-deps --user root \
      -v "$PWD/heycapy-backups/heycapy-2026-10-04.db:/restore.db:ro" \
      --entrypoint sh heycapy -c '
        mkdir -p /data/before-restore
        mv /data/heycapy.db* /data/before-restore/ 2>/dev/null
        cp /restore.db /data/heycapy.db
        chown nextjs:nodejs /data/heycapy.db'
    
  3. start it again and sign in:

    docker compose up -d
    

the database you replaced is kept in before-restore/ inside the volume, so a wrong pick can be undone. delete that folder once you’re sure.

don’t copy the file in with docker compose cp instead: the old heycapy.db-wal stays behind and is applied on top, which silently undoes the restore, and the file ends up owned by the wrong user so heycapy can’t write to it.

restoring after a bad upgrade: git checkout the previous version first, then restore, then docker compose up -d --build. restoring a backup made by a newer version into an older one isn’t supported.

© 2026 heycapy · mit licensed · all rights free. take it, run it, change it.