---
title: "Deploy Tinode"
description: "Instant messaging server with a web app and mobile SDKs"
category: "Other"
url: https://railway.com/deploy/tinode
---

# Deploy Tinode

Instant messaging server with a web app and mobile SDKs

**[Deploy Tinode on Railway](https://railway.com/template/tinode)**

Machine-readable deploy manifest (JSON, validated by TemplateCI): https://railway.com/deploy/tinode/manifest.json

- **Creator:** A3A
- **Category:** Other

## Template content

### tinode https://raw.githubusercontent.com/tinode/webapp/master/img/logo.svg

- **Source:** https://github.com/gridalpha/tinode-railway
- **Health check:** /
- **Public domain:** Yes

### Postgres https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/postgresql.svg

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

## Buckets

- **tinode-media**

## Documentation

# Deploy and Host Tinode on Railway

Tinode is an open-source instant messaging server written in Go. It is a chat *backend* rather than a finished chat product: the server owns accounts, topics, presence and delivery receipts and exposes them over websockets, long polling and gRPC, while first-party SDKs for JavaScript, Android and iOS do the client half. Teams reach for it when they need messaging inside their own application rather than renting a proprietary chat API, and a polished web app ships alongside.

Self-host Tinode on Railway and this template wires up everything the server needs. The `tinode` service runs the chat server and serves the bundled web app on its public domain. `Postgres` is Railway's managed PostgreSQL and holds every account, topic, subscription and message. A managed bucket, `tinode-media`, holds attachments: anything above the in-band message limit is uploaded through the server and handed back as a short-lived signed URL. The server builds its own schema on the first deployment and seeds one administrator account from the credentials you supply, so nothing is left to set up by hand.

![Tinode chat server connected to a Postgres database on Railway](https://res.cloudinary.com/rroe4rtk/image/upload/v1788872178/tinode-architecture.png)

## Getting Started with Tinode on Railway

Open the `tinode` service's public URL and you land on the web app's sign-in screen. Sign in with the `TINODE_ROOT_LOGIN` and `TINODE_ROOT_PASSWORD` you set at deploy time — that account is created once, on the deployment that builds the schema, and has root privileges. There are no demo accounts and no default password. To confirm messaging works, open a second browser profile, click the *add person* icon and register an ordinary account, then use **Start New Chat → find** and search for `basic:` — directory search matches prefixed tags, so a bare username returns nothing. Click the result and send a message: one tick means the server stored it, two means it was read.

From the admin session, click the chat icon and choose **new group** to create a group topic with a name, description and search tags; the same screen sets whether it behaves as a broadcast channel. Use the paperclip to attach a file: small ones travel inside the message, larger ones go out of band into the bucket, and a file that downloads back byte-for-byte proves storage works. Registration is open by default; set `SMTP_SERVER` to require email verification and `SMTP_DOMAINS` to restrict sign-ups to your own domains.

![Tinode direct message thread with an image and a file attachment](https://res.cloudinary.com/rroe4rtk/image/upload/v1788872180/tinode-direct-message.png)
![Tinode group topic carrying the launch team's posted messages](https://res.cloudinary.com/rroe4rtk/image/upload/v1788872182/tinode-group-topic.png)
![Tinode account settings panel showing profile and security sections](https://res.cloudinary.com/rroe4rtk/image/upload/v1788872186/tinode-account-settings.png)

## About Hosting Tinode

Tinode solves the problem of putting real chat into software that is not itself a chat app. Building messaging from scratch means reimplementing presence, ordering, delivery state and push; Tinode ships all of it behind one API and leaves the interface to you.

- Peer-to-peer, group and broadcast-channel topics with per-user access modes
- Delivery and read receipts, typing notifications, message editing and replies
- Out-of-band attachments with signed, expiring download URLs
- Websocket, long-polling and gRPC transports from one process
- Firebase push for mobile clients, and tag-based directory search
- A bundled React web app plus Android, iOS and JavaScript SDKs

The deployment is three pieces. `tinode` is the server and the web app, and the only service with a public domain. `Postgres` stores everything durable and is reachable only on the private network. `tinode-media` is object storage for attachments — the server signs a short-lived URL per download rather than opening the bucket.

## Why Deploy Tinode on Railway

Railway removes the parts of self-hosting Tinode that are not about chat.

- Managed PostgreSQL with backups, no database container to babysit
- Managed object storage for attachments, no MinIO to run or patch
- TLS, a public domain and a private network provisioned automatically
- Server keys generated per deployment, not the published sample values

## Common Use Cases

- In-app messaging for a marketplace, where buyers and sellers talk without sharing numbers
- A support inbox where each customer conversation is a topic your agents subscribe to
- Team chat for a group that wants its history in a database it controls
- The messaging layer behind a mobile app, driven from the Android, iOS or JavaScript SDK

## Dependencies for Tinode

- **tinode** — built from `gridalpha/tinode-railway`, a thin layer on `tinode/tinode-postgres`
- **Postgres** — Railway managed PostgreSQL; Tinode creates and owns its own database in it
- **tinode-media** — Railway managed S3-compatible object storage for attachments

### Environment Variables Reference

| Variable | Purpose |
|---|---|
| `TINODE_MASTER_SECRET` | Seeds the API key salt, token signing key and user-ID key; keep it stable |
| `TINODE_ROOT_LOGIN` | Login for the administrator account created on first deploy |
| `TINODE_ROOT_PASSWORD` | Password for that account; six characters or more |
| `PGDATABASE` | Database Tinode creates for itself; must not already exist |
| `MEDIA_HANDLER` | `s3` for object storage, `fs` for a local directory |
| `MAX_MESSAGE_SIZE` | Bytes; attachments above ~3/4 of it go to storage |
| `SMTP_SERVER` / `SMTP_DOMAINS` | Require email verification; limit sign-ups by domain |
| `UPGRADE_DB` | Applies schema migrations in place on upgrade |

### Deployment Dependencies

- Source repository: 
- Upstream: , web app 
- Docker image: `tinode/tinode-postgres`; runtime Go 1.26 and PostgreSQL 18

## Hardware Requirements for Self-Hosting Tinode

Tinode is a single Go binary and unusually light for what it does.

| Resource | Minimum | Recommended |
|---|---|---|
| CPU | 0.5 vCPU | 1–2 vCPU |
| RAM | 256 MB | 1 GB |
| Storage | Database only | Postgres volume plus a bucket for attachments |
| Runtime | Go 1.26, PostgreSQL 18 | Same |

Memory scales with concurrent sessions, not total accounts: idle users cost nothing between messages.

## Self-Hosting Tinode

Upstream publishes one image per database backend. The quickest local run pairs the PostgreSQL build with a database container:

```
docker run -d --name tinode-db -e POSTGRES_PASSWORD=changeme postgres:18-alpine
docker run -d --name tinode -p 6060:6060 --link tinode-db \
  -e POSTGRES_DSN='postgresql://postgres:changeme@tinode-db:5432/tinode?sslmode=disable' \
  tinode/tinode-postgres:latest
```

The web app is then on `http://localhost:6060/`. Before exposing it, replace the three keys the published image ships as samples and regenerate the API key to match the new salt — the key is an HMAC of the salt, so changing one without the other makes every client fail authentication. The image carries the generator:

```
/opt/tinode/keygen -salt "$(openssl rand -base64 32)"
```

Put the printed salt in `api_key_salt` and the printed key in the client bundle. Building from source needs Go 1.26 and a checkout of `github.com/tinode/chat`.

## How Much Does Tinode Cost to Self-Host?

Tinode is free and open source under the GPL-3.0 licence, with no paid edition, seat limits or feature gates — every capability described here is in the build this template deploys. The project offers an optional hosted push gateway for mobile notifications; it is the only paid service in the ecosystem and is not required. On Railway you pay for infrastructure only: the container, the PostgreSQL instance and the storage you use.

## FAQ

**What is Tinode?**
Tinode is an open-source instant messaging server written in Go. It provides accounts, one-to-one and group topics, presence, delivery receipts and attachments over websocket, long-polling and gRPC APIs, with official web, Android and iOS clients.

**What does this Railway template deploy?**
The Tinode server with its bundled web app on a public domain, a managed PostgreSQL database on the private network, and an object storage bucket for attachments. Server keys are generated for your deployment and one administrator account is seeded from the credentials you supply.

**Why does the template include PostgreSQL and object storage?**
Tinode keeps every account, topic, subscription and message in a relational database, so it cannot run without one. Attachments stay out of it on purpose: multi-megabyte files stored as message content bloat the tables, so anything above the in-band limit goes to the bucket behind a signed, expiring URL.

**How do I connect my own app to self-hosted Tinode?**
Point the official JavaScript, Android or iOS SDK at your deployment's public domain and pass the API key from the deployed web app bundle. Server-side clients can use gRPC by setting `GRPC_LISTEN`, which needs a TCP proxy rather than the HTTP domain.

**Can I stop strangers from registering on my self-hosted Tinode server?**
Tinode has no switch that disables sign-up. Set `SMTP_SERVER` and the related mail variables to require email verification, then set `SMTP_DOMAINS` to domains you control so only addresses there can register.

**How do I upgrade Tinode without losing messages?**
Redeploy with a newer image. The server checks its schema version at start-up and migrates in place because `UPGRADE_DB` is enabled. Take a database backup first, as with any schema change.


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