Deploy It's a Plan
Project tracking with boards, cycles, docs, and AI agents
Just deployed
Just deployed
Just deployed
gateway
Just deployed
Just deployed
/backups
Just deployed
/var/lib/postgresql/data
attachments
Bucket
Just deployed
Deploy and Host It's a Plan on Railway
It's a Plan is an open-source, self-hosted project management and issue tracking application for teams and AI agents. It provides configurable boards, cycles, initiatives, documents, dashboards, REST and MCP APIs, webhooks, and notifications. This template includes the browser interface, backend, background services, persistent PostgreSQL, and attachment storage on Railway.
About Hosting It's a Plan on Railway
This Docker-only template runs the upstream application images at version 1.7.0. A Caddy gateway serves the web interface and API from one HTTPS hostname. PostgreSQL stores application data, and a Railway S3-compatible bucket stores attachments. The API applies database migrations through its original image command.
Tech Stack
- Bun, Turborepo, and Elysia for the backend and background services.
- Next.js with server-side rendering, React, shadcn/ui, and Tailwind CSS.
- better-auth for email/password authentication and optional identity providers.
- Drizzle ORM and PostgreSQL 18.
- Railway S3-compatible object storage and Caddy 2.10.2.
Why Deploy It's a Plan on Railway
Railway provides HTTPS, private service networking, persistent database volumes, generated secrets, and object storage. The template pulls published Docker images without building source. Its gateway allows authentication on a generated Railway domain, so a custom domain is optional.
Common Use Cases
- Track software issues, releases, cycles, and product initiatives.
- Organize team work using boards, documents, and dashboards.
- Connect AI agents through REST APIs and MCP.
- Deliver webhook notifications and optional Telegram messages.
Deployment Notes
Open the gateway domain after all services become healthy. Its public port is 8080; the web application listens on 3001, and the API listens on 3000. The gateway strips the leading /api before forwarding API requests. Authentication therefore uses /api/api/auth/*. Background workers and the Telegram bot have no public listener and no PORT variable.
Register your own account on the first visit. The first registered account becomes the instance administrator. Upstream has no preset administrator credentials or supported administrator bootstrap username/password environment variables. Configure SMTP, AI provider credentials, and the Telegram bot token in the application interface. The bot remains idle until configured; keep exactly one replica.
The gateway is the intended browser entry point. api.API_URL=https://${{gateway.RAILWAY_PUBLIC_DOMAIN}}/api and api.APP_URL=https://${{gateway.RAILWAY_PUBLIC_DOMAIN}} use one hostname, allowing secure host-only session cookies. web.API_URL references api.API_URL. The generated API and web domains are diagnostic endpoints. To use a custom domain, attach it to the gateway and update the API's API_URL and APP_URL to that hostname; API_URL must retain /api.
Preserve BETTER_AUTH_SECRET and APP_ENCRYPTION_KEY; each is generated with ${{secret(64)}}. The worker and bot reference the API encryption key. PostgreSQL generates its own password, and all application services reference Postgres.DATABASE_URL. Changing an established encryption key prevents stored credentials from being decrypted.
Persist PostgreSQL at /var/lib/postgresql/data with PGDATA=/var/lib/postgresql/data/pgdata. Persist pre-migration dumps on the API's /backups volume, with BACKUP_RETENTION_DAYS=30. The API image includes PostgreSQL client tools and retains its original migration-and-server command. Web, worker, and bot require no application volume.
Create the native Railway bucket named attachments; it is included in the published template. The API references its ENDPOINT, BUCKET, ACCESS_KEY_ID, SECRET_ACCESS_KEY, and REGION variables. S3_FORCE_PATH_STYLE=false enables Railway's virtual-host addressing. The worker references the same API S3 configuration. Bucket credentials must remain references, never copied literal keys.
Required variable defaults and references:
| Service | Variables |
|---|---|
| gateway | PORT=8080; API_UPSTREAM=${{api.RAILWAY_PRIVATE_DOMAIN}}:3000; WEB_UPSTREAM=${{web.RAILWAY_PRIVATE_DOMAIN}}:3001; CADDY_CONFIG contains the supplied routing configuration. |
| api | NODE_ENV=production; PORT=3000; API_PORT=3000; DATABASE_URL=${{Postgres.DATABASE_URL}}; generated BETTER_AUTH_SECRET and APP_ENCRYPTION_KEY; API_URL and APP_URL reference the gateway; CLIENT_IP_HEADER=x-real-ip; BACKUP_DIR=/backups; BACKUP_RETENTION_DAYS=30; all six S3_* variables listed above. |
| web | NODE_ENV=production; PORT=3001; HOSTNAME=0.0.0.0; API_URL=${{api.API_URL}}; SERVICE_URL_API=http://${{api.RAILWAY_PRIVATE_DOMAIN}}:3000. |
| worker | NODE_ENV=production; DATABASE_URL=${{Postgres.DATABASE_URL}}; APP_ENCRYPTION_KEY=${{api.APP_ENCRYPTION_KEY}}; all six S3_* values reference the API; TELEMETRY_DISABLED=1. |
| bot | NODE_ENV=production; DATABASE_URL=${{Postgres.DATABASE_URL}}; APP_ENCRYPTION_KEY=${{api.APP_ENCRYPTION_KEY}}. |
| Postgres | POSTGRES_USER=postgres; POSTGRES_DB=railway; generated POSTGRES_PASSWORD; PGDATA, PGUSER, PGPASSWORD, PGHOST, PGPORT, PGDATABASE, and DATABASE_URL use the supplied internal connection defaults; SSL_CERT_DAYS=820; RAILWAY_DEPLOYMENT_DRAINING_SECONDS=60. |
Keep the supplied Caddy start command; it reads CADDY_CONFIG from standard input. All four application services and PostgreSQL use their image defaults. Initial worker or bot polls can retry while the API applies the first database migrations. Image tags are pinned; upgrade application images together after reviewing the release notes.
The verified deployment is available here. Verification covered service status, startup logs, sign-in and session persistence, an authenticated homepage, database reads, and attachment upload/download through both the API and web media route. Temporary verification data was removed.
Upstream: GitHub repository. See Railway bucket variables, Caddy startup options, and upstream hosting documentation.
Dependencies for It's a Plan on Railway
The gateway connects to the web and API services over Railway's private network. The API, worker, and bot share PostgreSQL; the API and worker share the attachment bucket. Two volumes preserve database data and pre-migration backups. The native bucket is a required managed resource in addition to the Docker services.
Deployment Dependencies
| Service | Image | Port | Volume |
|---|---|---|---|
| gateway | caddy:2.10.2 | 8080 | - |
| web | ghcr.io/croffasia/itsaplan-web:1.7.0 | 3001 | - |
| api | ghcr.io/croffasia/itsaplan-api:1.7.0 | 3000 | /backups |
| worker | ghcr.io/croffasia/itsaplan-worker:1.7.0 | - | - |
| bot | ghcr.io/croffasia/itsaplan-bot:1.7.0 | - | - |
| Postgres | ghcr.io/railwayapp-templates/postgres-ssl:18 | 5432 (private) | /var/lib/postgresql/data |
| attachments | Railway managed S3-compatible bucket | HTTPS | Managed object storage |
Template Content
