How to Self-Host Appwrite on a VPS: Backend as a Service with Auth, Database, and Storage

How to Self-Host Appwrite on a VPS: Backend as a Service with Auth, Database, and Storage

Appwrite is an open-source Backend as a Service (BaaS) that provides authentication (30+ OAuth providers), a document database with real-time subscriptions, file storage, serverless functions, push notifications, and team management — all in one Docker Compose stack. It competes directly with Firebase and Supabase. Unlike Supabase (PostgreSQL-based), Appwrite uses its own document database and MariaDB internally, making it simpler to self-host on smaller VPS instances.

Appwrite vs Supabase vs Pocketbase

Factor Appwrite Supabase Pocketbase
Database Document DB (NoSQL-like) PostgreSQL (SQL) SQLite
Min RAM 2 GB 4 GB 32 MB
Functions Yes (30+ runtimes) Yes (Deno/Postgres) JS hooks only
Realtime Yes (all resources) Yes (PostgreSQL) Yes (SSE)
Auth providers 30+ OAuth 20+ OAuth OAuth2 + custom
Setup complexity Medium High Very low

Server Requirements

  • Minimum 2 GB RAM (3–4 GB recommended for production)
  • Docker and Docker Compose
  • 20+ GB NVMe storage

Step 1: Install Appwrite

<code">mkdir -p /opt/appwrite && cd /opt/appwrite

docker run -it --rm \
    --volume /var/run/docker.sock:/var/run/docker.sock \
    --volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \
    --entrypoint="install" \
    appwrite/appwrite:latest

The interactive installer asks:

  • HTTP port: 80 (or leave default)
  • HTTPS port: 443
  • Hostname: appwrite.yourdomain.com
  • DNS resolver: default
  • Skip SSL: No — provide your own via Nginx

Alternatively, use the manual Docker Compose approach:

<code">curl -o docker-compose.yml https://appwrite.io/install/compose
curl -o .env https://appwrite.io/install/env
nano .env
<code"># Required changes in .env:
_APP_ENV=production
_APP_OPENSSL_KEY_V1=your_32_char_key   # openssl rand -hex 16
_APP_DOMAIN=appwrite.yourdomain.com
_APP_DOMAIN_TARGET=appwrite.yourdomain.com
_APP_SMTP_HOST=smtp.mailgun.org
_APP_SMTP_PORT=587
_APP_SMTP_SECURE=tls
_APP_SMTP_USERNAME=postmaster@mg.yourdomain.com
_APP_SMTP_PASSWORD=your_smtp_password
_APP_SMTP_FROM=noreply@yourdomain.com
<code">docker compose up -d
# Appwrite starts multiple services — wait 2–3 minutes
docker compose ps

Step 2: Configure Nginx for SSL

Appwrite runs its own Traefik internally on ports 80 and 443. To put Nginx in front (with your existing Certbot certs):

<code"># First, change Appwrite's Traefik to use non-standard ports
# Edit .env:
_APP_PORT=8080
_APP_HTTPS_PORT=8443

# Restart
docker compose up -d
<code">sudo nano /etc/nginx/sites-available/appwrite
<code">server {
    listen 443 ssl http2;
    server_name appwrite.yourdomain.com;

    ssl_certificate /etc/letsencrypt/live/appwrite.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/appwrite.yourdomain.com/privkey.pem;

    client_max_body_size 100M;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_read_timeout 300s;
    }
}
<code">sudo certbot --nginx -d appwrite.yourdomain.com
sudo ln -s /etc/nginx/sites-available/appwrite /etc/nginx/sites-enabled/
sudo systemctl reload nginx

Step 3: Create First Project

  1. Visit https://appwrite.yourdomain.com
  2. Create admin account (first user becomes admin)
  3. Create Project → name it → add platform (Web, Flutter, Android, iOS)
  4. Copy the Project ID and API Endpoint

Step 4: Configure Authentication

  1. Project → Auth → Settings
  2. Enable desired methods: Email/Password, Magic URL, Google, GitHub, Apple
  3. For OAuth providers: add Client ID and Secret from respective developer consoles
  4. Configure allowed redirect URLs for OAuth flows

Step 5: Create a Database Collection

<code"># Via Appwrite SDK — create a collection programmatically
from appwrite.client import Client
from appwrite.services.databases import Databases
from appwrite.input_file import InputFile
from appwrite.id import ID
from appwrite.permission import Permission
from appwrite.role import Role

client = Client()
client.set_endpoint('https://appwrite.yourdomain.com/v1')
client.set_project('your_project_id')
client.set_key('your_api_key')

databases = Databases(client)

# Create database
db = databases.create(ID.unique(), 'Blog')

# Create collection
collection = databases.create_collection(
    database_id=db['$id'],
    collection_id=ID.unique(),
    name='posts',
    permissions=[
        Permission.read(Role.any()),      # Anyone can read
        Permission.create(Role.users()),   # Only logged-in users can create
    ]
)

# Add attributes
databases.create_string_attribute(db['$id'], collection['$id'], 'title', 255, required=True)
databases.create_string_attribute(db['$id'], collection['$id'], 'content', 65535, required=True)
databases.create_boolean_attribute(db['$id'], collection['$id'], 'published', required=False)

Step 6: JavaScript SDK Integration

<code">npm install appwrite
<code">//  client.js
import { Client, Account, Databases, Storage } from 'appwrite';

const client = new Client()
    .setEndpoint('https://appwrite.yourdomain.com/v1')
    .setProject('your_project_id');

export const account = new Account(client);
export const databases = new Databases(client);
export const storage = new Storage(client);

// Auth: create account
await account.create(ID.unique(), 'user@example.com', 'password123', 'John Doe');

// Auth: login
const session = await account.createEmailPasswordSession('user@example.com', 'password123');

// Database: create document
const post = await databases.createDocument(
    'blog_db_id',
    'posts_collection_id',
    ID.unique(),
    { title: 'Hello World', content: 'My first post', published: true }
);

// Database: list documents with filter
const posts = await databases.listDocuments('blog_db_id', 'posts_collection_id', [
    Query.equal('published', [true]),
    Query.orderDesc('$createdAt'),
    Query.limit(10),
]);

// Storage: upload file
const fileInput = document.getElementById('file');
const file = await storage.createFile('bucket_id', ID.unique(), fileInput.files[0]);

Appwrite Functions (Serverless)

<code"># Create a function in the Appwrite console
# Choose runtime: Node.js 20, Python 3.12, PHP 8.3, Dart, etc.
# Trigger: HTTP request, event (document.create, etc.), or schedule

# Example Node.js function:
export default async ({ req, res, log }) => {
    const { name } = JSON.parse(req.body);
    log(`Processing request for: ${name}`);
    return res.json({ greeting: `Hello, ${name}!` });
};

Getting Started

Appwrite needs 2 GB RAM minimum; 3–4 GB is comfortable for production with multiple active users. KVM VPS plans at VPS.DO with 4 GB RAM run Appwrite comfortably. NVMe storage benefits file storage performance and MariaDB query speed. Appwrite is an excellent choice for teams building mobile apps (Flutter support is first-class) or web apps that need more than a simple REST API without the SQL complexity of Supabase.

Conclusion

Self-hosted Appwrite provides a complete BaaS stack — authentication with 30+ OAuth providers, a document database with real-time subscriptions, file storage, and serverless functions — on a VPS at infrastructure cost. Its document database model is more accessible than Supabase’s SQL approach for teams without database expertise, while Pocketbase is the lighter-weight single-binary alternative for simpler use cases.

Fast • Reliable • Affordable VPS - DO It Now!

Get top VPS hosting with VPS.DO’s fast, low-cost plans. Try risk-free with our 7-day no-questions-asked refund and start today!