---
title: "Deploy It's a Plan"
description: "Project tracking with boards, cycles, docs, and AI agents"
category: "Other"
url: https://railway.com/deploy/its-a-plan-1
---

# Deploy It's a Plan

Project tracking with boards, cycles, docs, and AI agents

**[Deploy It's a Plan on Railway](https://railway.com/template/its-a-plan-1)**

Machine-readable deploy manifest (JSON, validated by TemplateCI): https://railway.com/deploy/its-a-plan-1/manifest.json

- **Creator:** kmaki's Projects
- **Category:** Other

## Template content

### bot https://raw.githubusercontent.com/croffasia/itsaplan/main/apps/web/public/icon-512.png

- **Image:** ghcr.io/croffasia/itsaplan-bot:1.7.0

### worker https://raw.githubusercontent.com/croffasia/itsaplan/main/apps/web/public/icon-512.png

- **Image:** ghcr.io/croffasia/itsaplan-worker:1.7.0

### web https://raw.githubusercontent.com/croffasia/itsaplan/main/apps/web/public/icon-512.png

- **Image:** ghcr.io/croffasia/itsaplan-web:1.7.0
- **Health check:** /login
- **Public domain:** Yes

### gateway https://cdn.simpleicons.org/caddy

- **Image:** caddy:2.10.2
- **Start command:** `sh -c 'printf "%s" "$CADDY_CONFIG" | exec caddy run --config - --adapter caddyfile'`
- **Health check:** /login
- **Public domain:** Yes

### api https://raw.githubusercontent.com/croffasia/itsaplan/main/apps/web/public/icon-512.png

- **Image:** ghcr.io/croffasia/itsaplan-api:1.7.0
- **Health check:** /
- **Public domain:** Yes

### Postgres https://devicons.railway.app/i/postgresql.svg

- **Image:** ghcr.io/railwayapp-templates/postgres-ssl:18

## Buckets

- **attachments**

## Documentation

# 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.

[![Deploy on Railway](https://railway.com/button.svg)](https://railway.com/deploy/its-a-plan-1)

## 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](https://gateway-production-b126.up.railway.app/). 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](https://github.com/croffasia/itsaplan). See [Railway bucket variables](https://docs.railway.com/storage-buckets), [Caddy startup options](https://caddyserver.com/docs/command-line), and [upstream hosting documentation](https://github.com/croffasia/itsaplan/blob/main/docs/railway.md).

## 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 |


## Similar templates

- [Rocky Linux](https://railway.com/deploy/rocky-linux) — Hosted Rocky Linux 9 workspace with SSH and persistent storage. 🚀
- [Foundry Virtual Tabletop](https://railway.com/deploy/X5tR6G) — A Self-Hosted & Modern Roleplaying Platform
- [Letta Code Remote](https://railway.com/deploy/letta-code-remote) — Run a Letta Code agent 24/7. No inbound ports, just deploy.

Open this page in a browser: https://railway.com/deploy/its-a-plan-1
