Deploy Tinode

Instant messaging server with a web app and mobile SDKs

Deploy Tinode

Just deployed

/var/lib/postgresql/data

tinode-media

Bucket

Just deployed

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

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 Tinode group topic carrying the launch team's posted messages Tinode account settings panel showing profile and security sections

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

VariablePurpose
TINODE_MASTER_SECRETSeeds the API key salt, token signing key and user-ID key; keep it stable
TINODE_ROOT_LOGINLogin for the administrator account created on first deploy
TINODE_ROOT_PASSWORDPassword for that account; six characters or more
PGDATABASEDatabase Tinode creates for itself; must not already exist
MEDIA_HANDLERs3 for object storage, fs for a local directory
MAX_MESSAGE_SIZEBytes; attachments above ~3/4 of it go to storage
SMTP_SERVER / SMTP_DOMAINSRequire email verification; limit sign-ups by domain
UPGRADE_DBApplies 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.

ResourceMinimumRecommended
CPU0.5 vCPU1–2 vCPU
RAM256 MB1 GB
StorageDatabase onlyPostgres volume plus a bucket for attachments
RuntimeGo 1.26, PostgreSQL 18Same

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.


Template Content

More templates in this category

View Template
Rocky Linux
Hosted Rocky Linux 9 workspace with SSH and persistent storage. 🚀

codestorm
47
View Template
Foundry Virtual Tabletop
A Self-Hosted & Modern Roleplaying Platform

Lucas
71
View Template
Letta Code Remote
Run a Letta Code agent 24/7. No inbound ports, just deploy.

Letta
51