
Deploy Kaneo | (Just Updated) Self-Hosted Project Management with REST API & MCP, Admin Pre-Created
Kaneo self-hosted project management, REST API, MCP, Postgres
postgres
Just deployed
/var/lib/postgresql
Just deployed
Deploy and Host Kaneo on Railway
Built for an LLM agent to deploy and use. The deploy form has no fields to fill: the instance administrator is created from generated credentials at first boot, public sign-up is closed before the first request is served, and the REST API takes a bearer token from a normal sign-in. Kaneo is an open-source, self-hosted project management tool: workspaces, projects, kanban boards, tasks, labels, time tracking, notifications, API keys and a built-in MCP server, on PostgreSQL.
About Hosting Kaneo
This template runs two services: kaneo (the web app and the REST API behind one public domain) and postgres (PostgreSQL 18 on a volume mounted at /var/lib/postgresql). The kaneo service keeps no state of its own; every workspace, project and task lives in the database.
Locked from boot. A stock Kaneo lets the first visitor to the URL register and become the instance administrator, regenerates its session signing secret (AUTH_SECRET) on every start when unset (every session dies on each redeploy), and its bundled nginx listens on a hard-coded port that Railway's proxy and healthcheck never reach. This template's image listens on Railway's PORT, refuses to start without a stable AUTH_SECRET, creates the administrator from ADMIN_EMAIL and ADMIN_PASSWORD before the sign-up route is reachable (it answers 503 until then) and then turns public registration off in the instance settings.
What persists. Users, workspaces, projects, tasks, comments and API keys live in the PostgreSQL volume. File attachments need an S3-compatible bucket (S3_* variables, see the Kaneo docs); without one, attachments are unavailable and everything else works.
Using the web interface. Open the public URL and sign in with the ADMIN_EMAIL and ADMIN_PASSWORD values from the kaneo service variables.
Agent contract
- Public URL:
https://RAILWAY_PUBLIC_DOMAIN(the generated domain of thekaneoservice) - Private URL:
http://kaneo.railway.internal:8080 - Credentials: the variables
ADMIN_EMAILandADMIN_PASSWORDon thekaneoservice - Auth: sign in with
POST /api/auth/sign-in/email(JSONemail,password); the response headerset-auth-tokenis a token to send asAuthorization: Bearer TOKENon every/apirequest, and without it the API answers401. After sign-in an agent can also create an API key (x-api-keyheader) from the web interface - Health:
GET /api/healthreturns 200 without credentials
# needs: SITE_URL (public https URL of the kaneo service), ADMIN_EMAIL and ADMIN_PASSWORD (its variables); also curl, sed
login() { curl -s -i -X POST "$SITE_URL/api/auth/sign-in/email" -H "Content-Type: application/json" -d "{\"email\":\"$ADMIN_EMAIL\",\"password\":\"$ADMIN_PASSWORD\"}" | tr -d '\r' | sed -n 's/^set-auth-token: //Ip'; }
TOKEN=$(login)
AUTH="Authorization: Bearer $TOKEN"
curl -s -o /dev/null -w "health: %{http_code}\n" "$SITE_URL/api/health"
curl -s -o /dev/null -w "read without key: %{http_code}\n" "$SITE_URL/api/project"
curl -s -o /dev/null -w "write without key: %{http_code}\n" -X POST -H "Content-Type: application/json" -d '{"name":"x","workspaceId":"x","icon":"Layout","slug":"x"}' "$SITE_URL/api/project"
curl -s -o /dev/null -w "write wrong key: %{http_code}\n" -X POST -H "Authorization: Bearer wrong" -H "Content-Type: application/json" -d '{"name":"x","workspaceId":"x","icon":"Layout","slug":"x"}' "$SITE_URL/api/project"
curl -s -o /dev/null -w "public sign-up: %{http_code}\n" -X POST -H "Content-Type: application/json" -d '{"email":"intruder@example.com","password":"Sup3rSecretPassw0rd","name":"x"}' "$SITE_URL/api/auth/sign-up/email"
WSID() { curl -s -H "$AUTH" "$SITE_URL/api/workspace" | sed -n 's/.*"id":"\([^"]*\)".*/\1/p'; }
WS=$(WSID)
if [ -z "$WS" ]; then curl -s -o /dev/null -X POST -H "$AUTH" -H "Content-Type: application/json" -d '{"name":"Agent","slug":"agent"}' "$SITE_URL/api/auth/organization/create"; WS=$(WSID); fi
echo "workspace: $WS"
PID=$(curl -s -X POST -H "$AUTH" -H "Content-Type: application/json" -d "{\"name\":\"Agent check\",\"workspaceId\":\"$WS\",\"icon\":\"Layout\",\"slug\":\"agent-check\"}" "$SITE_URL/api/project" | sed -n 's/^{"id":"\([^"]*\)".*/\1/p')
echo "project created: $PID"
TID=$(curl -s -X POST -H "$AUTH" -H "Content-Type: application/json" -d '{"title":"agent-check task","description":"created by the agent contract","priority":"low","status":"to-do"}' "$SITE_URL/api/task/$PID" | sed -n 's/^{"id":"\([^"]*\)".*/\1/p')
echo "task created: $TID"
{ curl -s -H "$AUTH" "$SITE_URL/api/task/tasks/$PID"; echo; } | sed -n 's/.*"title":"\(agent-check task\)".*/task read: \1/p'
curl -s -o /dev/null -w "task delete: %{http_code}\n" -X DELETE -H "$AUTH" "$SITE_URL/api/task/$TID"
curl -s -o /dev/null -w "project delete: %{http_code}\n" -X DELETE -H "$AUTH" "$SITE_URL/api/project/$PID"
A fresh deploy prints health: 200, read without key: 401, write without key: 401, write wrong key: 401, public sign-up: 403, a workspace id, a created project id, a created task id, task read: agent-check task, task delete: 200 and project delete: 200.
Why Deploy Kaneo on Railway?
Railway is a singular platform to deploy your infrastructure stack. Railway will host your infrastructure so you don't have to deal with configuration, while allowing you to vertically and horizontally scale it.
By deploying Kaneo on Railway, you are one step closer to supporting a complete full-stack application with minimal burden. Host your servers, databases, AI agents, and more on Railway.
- Agent-ready - no deployer fields, a contract block and one script that proves a sign-in, a write, a read, a delete and the refusal without a token.
- Locked by default - the deploy cannot start as an open instance where the first visitor becomes administrator.
- Sessions survive redeploys - the signing secret is generated once and kept stable.
- Data survives redeploys - PostgreSQL sits on a volume.
- Pinned - both images are pinned by digest.
- Your tasks stay yours - no SaaS seat pricing, no cloud account.
Common Use Cases
- Team or personal project management - boards, tasks, labels and time tracking without a per-seat subscription.
- Agent-driven task tracking - a coding or ops agent files, updates and closes tasks over the REST API or the MCP server.
- Jira or Linear alternative - a lightweight self-hosted tracker you control.
- Client and side-project boards - one workspace per client, data on your own infrastructure.
Dependencies for Kaneo Hosting
- PostgreSQL, deployed alongside as the
postgresservice. An S3-compatible bucket is optional (attachments). A custom domain is optional.
Deployment Dependencies
Template Content
postgres
postgres:18.6