Appearance
Deploy hooka on Railway
The hooka Railway template starts a complete hooka in one click:
| Service | What it is |
|---|---|
| hooka | hookad: the HTTP API, Telegram delivery and updates, reminders, and the app store |
| Postgres | Message history, routes, tokens, and everything else hooka stores (with a volume) |
| Hooka Dashboard | The Next.js control plane: topics, routes, tokens, forms, reminders, docs |
You need only a Telegram bot token. Railway generates the master API key, the dashboard passphrase, and the cookie secret for you.
1. Create a Telegram bot
- Open @BotFather in Telegram and send
/newbot. - Pick a name and a username. BotFather replies with a token such as
123456789:AA....
Use a bot that no other server is using. Telegram delivers updates to only one long-polling client per bot, so a second hooka on the same token makes both fail with 409 Conflict.
2. Deploy
- Click Deploy on Railway above.
- Paste the token into
TELEGRAM_BOT_TOKEN. Leave the other values as they are. - Click Deploy. The first build takes a few minutes; Railway builds the server and the dashboard from source.
When it is done, all three services show Active on the project canvas.
3. Find your URLs and secrets
Open the project on Railway:
| You need | Where |
|---|---|
| API URL | hooka service → Settings → Networking (e.g. https://hooka-production-xxxx.up.railway.app) |
| Master API key | hooka service → Variables → API_KEY |
| Dashboard URL | Hooka Dashboard service → Settings → Networking |
| Dashboard passphrase | Hooka Dashboard service → Variables → DASHBOARD_PASSPHRASE |
Check the server:
bash
export HOOKA_URL="https://<your hooka domain>"
export HOOKA_API_KEY="<API_KEY>"
curl -s "$HOOKA_URL/health"
# {"db":"ok","status":"ok"}4. Send your first message
- In Telegram, open a chat with your bot and send
/chatid. It replies with the chat ID (for a direct message, that is your user ID). - Route a topic to that chat and publish:
bash
export CHAT_ID="<the id from /chatid>"
curl -s -X POST "$HOOKA_URL/topics/hello/routes" \
-H "Authorization: Bearer $HOOKA_API_KEY" \
-H "Content-Type: application/json" \
-d "{\"provider\":\"telegram\",\"chat_id\":\"$CHAT_ID\"}"
curl -s -X POST "$HOOKA_URL/publish/hello" \
-H "Authorization: Bearer $HOOKA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"title":"hooka is live","text":"Sent from Railway","priority":4}'The message arrives in Telegram. Sign in to the dashboard with the passphrase to see the hello topic, its route, and the delivery.
The CLI works too:
bash
go install github.com/guzus/hooka/cmd/hooka@latest # or a release binary (after the public release)
hooka publish hello --text "Deploy finished" # uses HOOKA_URL and HOOKA_API_KEY5. Recommended settings
Set these on the hooka service (Variables), then let Railway redeploy:
| Variable | Why |
|---|---|
TELEGRAM_ADMIN_USER_ID | Your numeric Telegram user ID (the /chatid answer in your DM with the bot). Lets you run admin bot commands such as /app install and /poll. |
TELEGRAM_DEFAULT_CHAT_ID | A chat that receives publishes to topics without routes. |
Mint scoped tokens for scripts and agents instead of handing out the master key: in the dashboard under Tokens, or with POST /tokens (see the API reference).
Updating
The services build from the main branch of guzus/hooka. To pick up a new version, redeploy each service from Railway (Deployments → Redeploy). Database migrations run automatically when hookad starts, and they are idempotent.
Costs
Railway bills by usage. A quiet hooka (server, dashboard, and Postgres) fits comfortably in Railway's Hobby plan; message volume and history retention drive Postgres storage. Retention is configurable with MESSAGE_RETENTION_DAYS (default 30).
Troubleshooting
| Symptom | Fix |
|---|---|
Logs show telegram getMe failed | The bot token is wrong or revoked. Update TELEGRAM_BOT_TOKEN and redeploy. |
Logs show 409 Conflict from Telegram | Another process uses the same bot token. Stop it, or give this hooka its own bot. |
| Dashboard login says the dashboard is not configured | DASHBOARD_PASSPHRASE or DASHBOARD_COOKIE_SECRET is missing on the dashboard service. |
| Dashboard pages show an API error | The dashboard reaches hooka over Railway's private network. Check that its HOOKA_URL is http://${{hooka.RAILWAY_PRIVATE_DOMAIN}}:8080 and HOOKA_API_TOKEN is ${{hooka.API_KEY}}. |
/health reports the database down | Check the Postgres service; DATABASE_URL on hooka should be ${{Postgres.DATABASE_URL}}. |
How the template is wired
These are the settings the template creates. They are useful if you build the same stack by hand or in another Railway project.
hooka (builds the repository's root Dockerfile; health check /health)
| Variable | Value |
|---|---|
TELEGRAM_BOT_TOKEN | (you provide it) |
API_KEY | ${{secret(64, "abcdef0123456789")}} (generated at deploy) |
DATABASE_URL | ${{Postgres.DATABASE_URL}} |
HOOKA_PUBLIC_URL | https://${{RAILWAY_PUBLIC_DOMAIN}} |
Hooka Dashboard (root directory web, its own Dockerfile; health check /login)
| Variable | Value |
|---|---|
HOOKA_URL | http://${{hooka.RAILWAY_PRIVATE_DOMAIN}}:8080 |
HOOKA_API_TOKEN | ${{hooka.API_KEY}} |
HOOKA_PUBLIC_URL | https://${{hooka.RAILWAY_PUBLIC_DOMAIN}} |
DASHBOARD_PASSPHRASE | ${{secret(24)}} (generated at deploy) |
DASHBOARD_COOKIE_SECRET | ${{secret(64, "abcdef0123456789")}} (generated at deploy) |
Postgres (ghcr.io/railwayapp-templates/postgres-ssl:18, volume at /var/lib/postgresql/data)
| Variable | Value |
|---|---|
POSTGRES_PASSWORD | generated at deploy |
PGDATA | ${{RAILWAY_VOLUME_MOUNT_PATH}}/pgdata |
DATABASE_URL | postgresql://postgres:${{POSTGRES_PASSWORD}}@${{RAILWAY_PRIVATE_DOMAIN}}:5432/postgres |
Neither app sets PORT: both listen on the port Railway injects (8080), and both public domains route to 8080. Both services restart on failure.