---
title: "Deploy Appwrite (Full Stack)"
description: "Appwrite 2.2 full stack: auth, DB, storage, realtime. Firebase alternative"
category: "Other"
url: https://railway.com/deploy/appwrite-full-stack
---

# Deploy Appwrite (Full Stack)

Appwrite 2.2 full stack: auth, DB, storage, realtime. Firebase alternative

**[Deploy Appwrite (Full Stack) on Railway](https://railway.com/template/appwrite-full-stack)**

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

- **Creator:** Auromations
- **Category:** Other
- **Total deploys:** 1

## Template content

### scheduler

- **Image:** appwrite/appwrite:2.2.0
- **Start command:** `/usr/local/bin/schedule`

### postgresql

- **Image:** appwrite/postgres:0.1.0

### realtime

- **Image:** appwrite/appwrite:2.2.0
- **Start command:** `/usr/local/bin/realtime`

### maintenance

- **Image:** appwrite/appwrite:2.2.0
- **Start command:** `/usr/local/bin/maintenance`

### appwrite

- **Image:** appwrite/appwrite:2.2.0
- **Start command:** `php /usr/src/code/app/http.php`

### redis

- **Image:** redis:7.4.7-alpine
- **Start command:** `redis-server --bind 0.0.0.0 :: --protected-mode no --maxmemory 512mb --maxmemory-policy allkeys-lru --maxmemory-samples 5`

### mongodb

- **Image:** mongo:8.2.5
- **Start command:** `/bin/sh -c "set -e; mkdir -p /data/db /data/configdb /data/keyfile /docker-entrypoint-initdb.d; if [ ! -f /data/keyfile/mongo-keyfile ]; then openssl rand -base64 756 > /data/keyfile/mongo-keyfile; fi; chmod 400 /data/keyfile/mongo-keyfile; chown 999:999 /data/keyfile/mongo-keyfile || true; echo Y29uc3QgYWRtaW5EYiA9IGRiLmdldFNpYmxpbmdEQignYWRtaW4nKTsKYWRtaW5EYi5jcmVhdGVVc2VyKHsKICB1c2VyOiBwcm9jZXNzLmVudi5NT05HT19JTklUREJfVVNFUk5BTUUsCiAgcHdkOiBwcm9jZXNzLmVudi5NT05HT19JTklUREJfUEFTU1dPUkQsCiAgcm9sZXM6IFt7IHJvbGU6ICdyZWFkV3JpdGUnLCBkYjogcHJvY2Vzcy5lbnYuTU9OR09fSU5JVERCX0RBVEFCQVNFIH1dCn0pOwo= | base64 -d > /docker-entrypoint-initdb.d/mongo-init.js; echo IyEvYmluL2Jhc2gKUlNfU1RBVFVTPSQobW9uZ29zaCAtLXVzZXJuYW1lIHJvb3QgLS1wYXNzd29yZCAiJHtNT05HT19JTklUREJfUk9PVF9QQVNTV09SRH0iIC0tYXV0aGVudGljYXRpb25EYXRhYmFzZSBhZG1pbiAtLXF1aWV0IC0tZXZhbCAndHJ5IHsgcnMuc3RhdHVzKCkub2sgfSBjYXRjaChlKSB7IDAgfScgMj4vZGV2L251bGwgfHwgZWNobyAwKQppZiBbICIkUlNfU1RBVFVTIiA9ICIxIiBdOyB0aGVuIGV4aXQgMDsgZmkKbW9uZ29zaCAtLXVzZXJuYW1lIHJvb3QgLS1wYXNzd29yZCAiJHtNT05HT19JTklUREJfUk9PVF9QQVNTV09SRH0iIC0tYXV0aGVudGljYXRpb25EYXRhYmFzZSBhZG1pbiAtLXF1aWV0IC0tZXZhbCAicnMuaW5pdGlhdGUoeyBfaWQ6ICdyczAnLCBtZW1iZXJzOiBbeyBfaWQ6IDAsIGhvc3Q6ICcke1JBSUxXQVlfUFJJVkFURV9ET01BSU46LTEyNy4wLjAuMX06MjcwMTcnIH1dIH0pIgpmb3IgaSBpbiAkKHNlcSAxIDE1KTsgZG8KICBTVD0kKG1vbmdvc2ggLS11c2VybmFtZSByb290IC0tcGFzc3dvcmQgIiR7TU9OR09fSU5JVERCX1JPT1RfUEFTU1dPUkR9IiAtLWF1dGhlbnRpY2F0aW9uRGF0YWJhc2UgYWRtaW4gLS1xdWlldCAtLWV2YWwgJ3RyeSB7IHJzLnN0YXR1cygpLm15U3RhdGUgfSBjYXRjaChlKSB7IDAgfScgMj4vZGV2L251bGwpCiAgWyAiJFNUIiA9ICIxIiBdICYmIGJyZWFrCiAgc2xlZXAgMgpkb25lCg== | base64 -d > /docker-entrypoint-initdb.d/rs-init.sh; exec docker-entrypoint.sh mongod --replSet rs0 --bind_ip_all --auth --keyFile /data/keyfile/mongo-keyfile"`

### geo

- **Image:** appwrite/geo:0.3.1

### autogravity

- **Image:** ghcr.io/appwrite/autogravity:0.0.10

### router

- **Image:** caddy:2-alpine
- **Start command:** `/bin/sh -c "echo ':80 {' > /etc/caddy/Caddyfile; echo 'handle /__health {' >> /etc/caddy/Caddyfile; echo 'respond 200' >> /etc/caddy/Caddyfile; echo '}' >> /etc/caddy/Caddyfile; echo 'handle /v1/realtime* {' >> /etc/caddy/Caddyfile; echo 'reverse_proxy {$REALTIME_HOST}:8080' >> /etc/caddy/Caddyfile; echo '}' >> /etc/caddy/Caddyfile; echo 'handle /v1/* {' >> /etc/caddy/Caddyfile; echo 'reverse_proxy {$APPWRITE_HOST}:8080' >> /etc/caddy/Caddyfile; echo '}' >> /etc/caddy/Caddyfile; echo 'handle /.well-known/* {' >> /etc/caddy/Caddyfile; echo 'reverse_proxy {$APPWRITE_HOST}:8080' >> /etc/caddy/Caddyfile; echo '}' >> /etc/caddy/Caddyfile; echo 'handle {' >> /etc/caddy/Caddyfile; echo 'reverse_proxy {$CONSOLE_HOST}:8080' >> /etc/caddy/Caddyfile; echo '}' >> /etc/caddy/Caddyfile; echo '}' >> /etc/caddy/Caddyfile; exec caddy run --config /etc/caddy/Caddyfile"`
- **Health check:** /__health
- **Public domain:** Yes

### interval

- **Image:** appwrite/appwrite:2.2.0
- **Start command:** `/usr/local/bin/interval`

### worker

- **Image:** appwrite/appwrite:2.2.0
- **Start command:** `/usr/local/bin/worker`

### console

- **Image:** appwrite/new:1.1.96-self-hosted

## Documentation

# Deploy and Host Appwrite on Railway

Appwrite is an open source backend-as-a-service: authentication, databases, file
storage, realtime subscriptions, and messaging behind one API, with a web console
to manage it all. This template deploys the full Appwrite 2.2 self-hosted stack as
13 wired services with one public domain.

## About Hosting Appwrite

Appwrite is normally a 30+ container Docker Compose stack with a traefik reverse
proxy routing by hostname and path. On Railway it runs as a set of small services:
the API, the console, realtime, a combined queue worker, scheduled tasks,
PostgreSQL (main database), MongoDB (DocumentsDB), Redis, and two Appwrite helper
services. A tiny Caddy router replaces traefik: it owns the public domain and
splits `/v1` API traffic from console pages and `/v1/realtime` websockets, so
SDKs, the console, and realtime all share one domain exactly like upstream.

First boot takes 2 to 4 minutes while MongoDB initializes its replica set and
Appwrite provisions its schemas. If the console loads before the API is ready,
wait a minute and refresh.

## Common Use Cases

- Backend for web and mobile apps: email/password and OAuth auth, teams,
  databases with permissions, file storage, realtime subscriptions.
- Self-hosted Firebase-style backend for prototypes and side projects.
- Internal tools backend with the console as admin UI.

## Dependencies for Appwrite Hosting

### Deployment Dependencies

No external accounts or keys are required. All secrets are generated at deploy
time. Everything runs on Railway's private network; only the router has a public
domain.

### Implementation Details

After deploy, open the public domain, create the root account (first signup),
then an organization and a project. Use `https:///v1` as the API
endpoint in your SDKs.

Deliberate cuts, honestly documented:

- Functions and Sites do not work. They need Docker socket access, which Railway
  does not expose.
- Usage statistics are disabled (`_APP_USAGE_STATS=disabled`). To re-enable, add
  your own ClickHouse and set `_APP_CONNECTIONS_DB_USAGE`,
  `_APP_CONNECTIONS_DB_EXECUTIONS`, and `_APP_USAGE_STATS=enabled`.
- Custom domains for projects do not work (upstream relies on traefik issuing
  certificates). The main domain is unaffected.
- Storage is local disk. Uploads and downloads work through the API service. For
  production, set `_APP_STORAGE_DEVICE=S3` and the `_APP_STORAGE_S3_*` variables.
- Email is not configured. Set `_APP_SMTP_*` on the `appwrite`, `worker`, and
  `maintenance` services for password recovery and invites.

Troubleshooting:

- Boot logs show a ~30 second "execution schema setup" retry loop. Harmless:
  that store backs function execution logs, which are not in use.
- Do not lower `_APP_WORKER_MAX_COROUTINES` below 8 on the worker; the queue
  connection pool is sized by it and jobs will start failing.
- The PostgreSQL volume must stay mounted at `/var/lib/postgresql` (PG18 layout).

## Why Deploy Appwrite on Railway?

Railway turns a 30-container compose file into a one-click deploy: private
networking between services, persistent volumes, generated secrets, and a single
managed domain with TLS. No VPS, no reverse proxy config, no certificate
management.


## 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/appwrite-full-stack
