# Railway

> Deploy Grovs on Railway from the template, or from the repository's infrastructure definition with the CLI.

Source: https://docs.grovs.io/docs/self-hosting/platforms/railway

[Deploy on Railway](https://railway.com/deploy/grovs-community)

## When to use

You want a managed platform with no server to maintain. Railway runs the API, dashboard, two workers and three private database services with persistent volumes.

There are two flows. Use the **template** for a guided setup, or the **CLI** to manage configuration in files. The CLI flow also declares the custom domains for you. Pick one; do not deploy a second copy.

## Prerequisites

| Requirement         | Notes                                                                                                                                                                                                          |
| ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Railway plan        | Capacity for seven services and the custom domains below. Compute, volumes, network usage and your object storage are billable.                                                                                |
| Private S3 bucket   | Bucket-scoped credentials that can list, read, write and delete Grovs objects. The API and workers share the bucket; they cannot share a Railway volume ([Railway volumes](https://docs.railway.com/volumes)). |
| Domains you control | App, production links and test links                                                                                                                                                                           |
| CLI flow only       | Node.js 22 or later, the [Railway CLI](https://docs.railway.com/cli) and OpenSSL                                                                                                                               |

## 1. Prepare DNS and proxy

Choose three domains. This guide uses:

| Variable      | Example                  |
| ------------- | ------------------------ |
| `SERVER_HOST` | `grovs.example.com`      |
| `DOMAIN_LIVE` | `links.example.com`      |
| `DOMAIN_TEST` | `test.links.example.com` |

Railway issues the certificates. You add the DNS and verification records it gives you in step 4. Project links use `*.links.example.com` and `*.test.links.example.com`, and each needs its own domain and certificate setup ([Railway wildcard domains](https://docs.railway.com/networking/domains/working-with-domains)).

## 2. Create the app

**Template**

The template stores image references apart from runtime variables. Check the image sources of the application services. To follow the latest stable GHCR images, use:

| Services                      | Image source                        |
| ----------------------------- | ----------------------------------- |
| `web`, `worker-1`, `worker-2` | `ghcr.io/grovs-io/backend:latest`   |
| `dashboard`                   | `ghcr.io/grovs-io/dashboard:latest` |

For a fixed pair, replace `latest` on all four services with the version from the [latest completed release](https://github.com/grovs-io/self-host/releases/latest). A `GROVS_VERSION` runtime variable alone does not change a template service's image source. Leave the database image versions as supplied.

Open **Deploy on Railway** and choose your workspace.

**CLI**

Clone the repository and generate the private configuration:

```bash
git clone https://github.com/grovs-io/self-host.git
cd self-host
npm --prefix .railway ci
GROVS_RELEASE_URL="$(curl -fsSL -o /dev/null -w '%{url_effective}' https://github.com/grovs-io/self-host/releases/latest)"
GROVS_VERSION="${GROVS_RELEASE_URL##*/}" \
  GROVS_DOMAIN=grovs.example.com GROVS_LINKS_DOMAIN=links.example.com \
  GROVS_ADMIN_EMAIL=admin@example.com \
  ./scripts/prepare-railway.sh "$HOME/grovs-railway"
export GROVS_CONFIG_FILE="$HOME/grovs-railway/.env"
```

The file holds generated secrets and your first admin login. Keep it outside Git and back it up. A fresh setup selects the latest completed release; later runs preserve the file. The CLI definition requires a numbered `GROVS_VERSION`.

Create a new empty Railway project and environment. Keep all services and their volumes in one region.

## 3. Configure environment

**Template**

Fill in the eight required variables on `web`:

`SERVER_HOST`, `DOMAIN_LIVE`, `DOMAIN_TEST`, `BOOTSTRAP_ADMIN_EMAIL`, `AWS_S3_KEY_ID`, `AWS_S3_ACCESS_KEY`, `AWS_S3_REGION`, `AWS_S3_BUCKET`

For S3-compatible storage, also set `S3_ENDPOINT` and `S3_FORCE_PATH_STYLE` as needed. Railway generates the database passwords, application secrets and admin password. Keep these values when you redeploy or upgrade.

**CLI**

Add the bucket to the same file by uncommenting its example lines:

```dotenv
AWS_S3_KEY_ID=your-access-key
AWS_S3_ACCESS_KEY=your-secret-key
AWS_S3_REGION=your-bucket-region
AWS_S3_BUCKET=your-bucket-name
```

S3-compatible storage also accepts `S3_ENDPOINT` and `S3_FORCE_PATH_STYLE`. This file is the source for managed variables; change settings there.

## 4. Deploy and verify

**Template**

1. Review the services, storage and estimated usage, then deploy.

2. In **Networking**, add the custom domains, all targeting port **3000**:

   | Service     | Custom domains                                                                                                                                                                    |
   | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
   | `dashboard` | `dashboard.grovs.example.com`                                                                                                                                                     |
   | `web`       | `api.grovs.example.com`, `sdk.grovs.example.com`, `mcp.grovs.example.com`, `go.grovs.example.com`, `preview.grovs.example.com`, `*.links.example.com`, `*.test.links.example.com` |

3. Add the DNS and certificate-verification records Railway provides.

4. Sign in at your dashboard domain with the admin email and the generated `BOOTSTRAP_ADMIN_PASSWORD` from `web`'s variables.

**CLI**

Link the directory to the new project, then review and apply:

```bash
railway login
railway link
railway config plan
railway config apply
```

Check the linked project and environment and the proposed resources. `apply` asks for confirmation. This uses Railway's current project-level [IaC](https://docs.railway.com/infrastructure-as-code), not the deprecated per-service `railway.json` format.

The definition requests the dashboard host on `dashboard`, and the API, SDK, MCP, go and preview hosts plus both wildcard domains on `web`, all on port 3000. In each service's **Networking** settings, add the exact DNS records Railway supplies, including certificate-verification CNAMEs.

Open `https://dashboard.grovs.example.com` and use the login printed by setup.

Then verify:

1. `https://api.grovs.example.com/up` answers.
2. A project's production and test links open, and analytics arrive.
3. An uploaded image survives an API redeployment.

## Upgrades

1. Back up the databases, uploads and the private configuration. See [Backups](https://docs.grovs.io/docs/self-hosting/operate/backups#platforms).
2. Stop both workers. `worker-1` must have exactly one running scheduler, including during rollouts.
3. Select the release:
   - **CLI:** look up the latest release, change `GROVS_VERSION` in the private file, then plan and apply.
   - **Template:** update or pull the service image sources listed in step 2.
4. Deploy the API and confirm migrations succeed.
5. Restart the workers and dashboard on the matching release.

Moving `:latest` tags take effect on an image pull and deployment, not on running containers.

## Platform-specific gotchas

- The API initializes PostgreSQL and ClickHouse before starting (in the CLI flow, as a pre-deploy command). If a datastore is still starting on the first attempt, wait until it is ready and redeploy `web`. Check the logs to tell readiness failures from migration errors.
- Workers wait for API health. Keep `worker-1` at exactly one instance, and keep application services always running: no serverless sleeping for workers.
- Volumes mount at PostgreSQL's `/var/lib/postgresql/data`, Redis's `/data` and ClickHouse's `/var/lib/clickhouse`. Redis uses AOF and `noeviction`. Databases have no public domains or TCP proxies.
- Avoid `--show-values`; it can print secrets.
- If you use Cloudflare DNS, keep the verification records unproxied.
- Deleting services or volumes can delete data. Export your databases and review the deletion plan first. Removing a Railway project does not remove an external S3 bucket or its charges.
