Self-Hosting Appwrite on Linux VPS in Pakistan: The Open-Source Firebase Alternative for Mobile & Web Apps

Complete production guide to deploying self-hosted Appwrite on Linux VPS in Pakistan. Build modern Flutter, React Native, and Next.js applications with unlimited auth, real-time database, storage, and serverless functions without recurring USD fees.

Self-Hosting Appwrite on Linux VPS in Pakistan: The Open-Source Firebase Alternative for Mobile & Web Apps

Mobile and web application development in Pakistan has expanded rapidly over the past decade. Software agencies in Lahore, Karachi, and Islamabad build mission-critical digital products for domestic startups and global enterprise clients. Whether engineering on-demand logistics apps, fintech payment wallets, healthcare booking portals, or multi-tenant SaaS dashboards, development teams rely heavily on Backend-as-a-Service (BaaS) architectures to accelerate time-to-market.

For years, Google Firebase served as the default BaaS platform for Flutter, React Native, Android, and iOS development. However, relying on Firebase in Pakistan has become an operational and financial liability. Macroeconomic volatility, rigid banking regulations, and unpredictable billing models have forced engineering teams to re-evaluate proprietary overseas cloud backends.

Appwrite is the premier open-source Backend-as-a-Service alternative to Firebase. Built on a modular, containerized architecture, Appwrite packages user authentication, schema-less document databases, real-time WebSocket subscriptions, secure object storage, scheduled cron jobs, and serverless cloud functions into a unified software stack.

Deploying Appwrite on an independent, high-performance Linux VPS liberates Pakistani software houses from variable foreign currency invoicing, eliminates unpredictable per-read/per-write cloud costs, and ensures complete sovereignty over user data.

This comprehensive guide delivers a production-grade blueprint for planning, deploying, tuning, securing, and maintaining self-hosted Appwrite backends on Ubuntu Linux VPS infrastructure in Pakistan.


Macroeconomic and Architectural Rationale: Why Move Away from Firebase?

The decision to migrate from Firebase to self-hosted Appwrite is driven by both financial constraints and technical sovereignty.

+-----------------------------------------------------------------------+
|                    Firebase Multi-Tenant Cloud                        |
|  - Per-document read/write metering                                   |
|  - Fluctuating monthly USD billing via foreign credit cards           |
|  - 5% to 10% advance withholding tax by Pakistani banks               |
|  - Rigid State Bank of Pakistan (SBP) foreign transaction caps        |
|  - Zero visibility into underlying server or database engine          |
+-----------------------------------+-----------------------------------+
                                    |
                                    v [ Migration Path ]
+-----------------------------------------------------------------------+
|                Self-Hosted Appwrite on Pure NVMe Linux VPS            |
|  - Predictable, flat-rate monthly hosting costs in local PKR          |
|  - Zero per-query, per-user, or per-document read/write fees          |
|  - Direct root access to Redis, MariaDB, and Swoole worker engines    |
|  - Complete ownership over user records, media, and encryption keys   |
|  - Direct local peering and low-latency network connectivity          |
+-----------------------------------------------------------------------+

1. Per-Operation Billing vs. Predictable Compute Budgeting

Firebase Cloud Firestore charges developers per document read, write, and delete operation. In dynamic mobile applications featuring continuous search queries, real-time listeners, and complex nested data structures, an accidental client-side infinite loop or unindexed collection query can rack up hundreds or thousands of dollars in billing within a single weekend.

In contrast, a self-hosted Appwrite instance running on a dedicated Linux VPS costs a fixed, predictable fee. Whether your Flutter application handles 10,000 queries or 10,000,000 queries per day, your underlying hardware cost remains completely unchanged.

2. Pakistani Banking Regulations and Currency Volatility

Under State Bank of Pakistan (SBP) foreign exchange regulations, corporate and individual credit cards are subject to strict annual and monthly spending limits for cross-border software subscriptions. Furthermore, commercial banks in Pakistan apply advance withholding taxes ranging from 5% (for active filers) to 10% (for non-filers) on all international digital payments, alongside foreign exchange transaction fees.

When the Pakistani Rupee (PKR) fluctuates against the US Dollar (USD), proprietary cloud bills fluctuate unpredictably. By provisioning self-hosted infrastructure with local billing options, software houses stabilize their cost of operations.

3. Data Sovereignty and Client Compliance

Many enterprise clients, particularly in the banking, public sector, and healthcare verticals, enforce strict data localization policies. Passing sensitive end-user information, phone numbers, and identity verification documents through multi-tenant US datacenters violates non-disclosure agreements (NDAs) and regulatory privacy frameworks. Appwrite keeps all customer tables, audit trails, and user credentials isolated within your own virtual or dedicated servers.


Appwrite Internal Architecture: Container Ecosystem Explained

Appwrite is not a monolithic application; it is an orchestrated microservices ecosystem running inside Docker containers managed by Docker Compose. Understanding each component is essential for diagnosing bottlenecks and fine-tuning production deployments.

                                 [ Public Internet ]
                                          |
                                          | Ports 80 & 443
                                          v
+-----------------------------------------------------------------------------------+
| Traefik Edge Reverse Proxy & SSL Gateway                                          |
| - Automated Let's Encrypt TLS renewal                                             |
| - WebSocket connection routing                                                    |
| - Rate limiting and SSL offloading                                                |
+-----------------------------------------+-----------------------------------------+
                                          |
                     +--------------------+--------------------+
                     |                                         |
                     v HTTP (Internal API)                     v WebSocket (Port 80/443)
+-------------------------------------------+ +-------------------------------------+
| Appwrite API Engine (PHP Swoole Daemon)   | | Appwrite Realtime Service (Swoole)  |
| - Authentication & RBAC Engine            | | - WebSocket state management        |
| - REST API Endpoints                      | | - Client pub/sub multiplexing       |
| - Schema Validation                       | | - Push payload dispatching          |
+---------------------+---------------------+ +------------------+------------------+
                      |                                          |
                      +--------------------+---------------------+
                                           |
                                           v
+-----------------------------------------------------------------------------------+
| Redis In-Memory Caching & Task Queues                                             |
| - Fast key-value document caching                                                 |
| - Redis Pub/Sub backplane for real-time events                                    |
| - Asynchronous job queues for background processing                               |
+------------------------------------------+----------------------------------------+
                                           |
         +---------------------------------+---------------------------------+
         |                                 |                                 |
         v                                 v                                 v
+------------------+             +-------------------+             +-----------------+
| Worker Daemons   |             | Database Storage  |             | Cloud Runtimes  |
| - Database sync  |             | (MariaDB 10.11+)  |             | Executor Engine |
| - Mail delivery  |             | - Document tables |             | - Docker socket |
| - Webhook dispatch|            | - User accounts   |             | - Node.js micro |
| - Certificate gen|             | - Audit logs      |             | - Python micro  |
| - Storage prune  |             | - InnoDB engine   |             | - Dart micro    |
+------------------+             +-------------------+             +-----------------+

Core Architecture Components

  1. Traefik Gateway: Serves as the high-performance ingress controller. It terminates incoming HTTPS connections, automatically negotiates and renews Let’s Encrypt SSL/TLS certificates, balances API traffic, and handles HTTP-to-HTTPS redirection.
  2. Appwrite Core API Engine: Powered by PHP Swoole, an asynchronous, event-driven coroutine framework. Unlike legacy Apache/mod_php setups that fork new processes per request, Swoole runs persistently in memory, executing thousands of concurrent API requests per second with minimal CPU footprint.
  3. Appwrite Realtime Daemon: A dedicated Swoole WebSocket server handling long-lived duplex connections from mobile and web SDKs. When a database document changes, this daemon broadcasts changes to connected Flutter, React Native, or web clients in sub-millisecond intervals.
  4. Redis Cache and Queue Broker: Functions as the operational backbone for caching frequently accessed permissions, tracking rate limits, acting as the pub/sub event bus, and maintaining worker job queues.
  5. MariaDB / PostgreSQL Relational Engine: Stores system settings, users, teams, document collections, metadata, and audit records. MariaDB uses optimized InnoDB tables to manage schemaless document trees.
  6. Background Worker Suite: Specialized containerized workers that pull tasks asynchronously from Redis queues:
    • appwrite-worker-mails: Handles transactional verification and password reset emails.
    • appwrite-worker-messaging: Manages push notifications and SMS integrations.
    • appwrite-worker-databases: Executes complex relational triggers and background schema indexing.
    • appwrite-worker-functions: Manages synchronous and asynchronous serverless executions.
    • appwrite-worker-deletes: Safely cleans up soft-deleted files, collections, and orphaned assets without blocking the primary API.
  7. OpenRuntimes Executor: Communicates with the Linux host’s Docker socket (docker.sock) to dynamically spin up sandboxed micro-containers for executing cloud functions written in Node.js, Python, Dart, PHP, and Ruby.

Production Prerequisites & Server Sizing

Appwrite’s containerized infrastructure requires reliable CPU and memory allocations. Below are recommended specifications based on expected production workloads.

Workload Tier Concurrent App Users VPS Configuration Recommended Storage Target Use Case
Development / MVP Up to 1,000 2 vCPU, 4 GB RAM 60 GB Pure NVMe Internal testing, staging environments, low-traffic portfolios
Standard Production 1,000 to 10,000 4 vCPU, 8 GB RAM 120 GB Pure NVMe Commercial mobile apps, client projects, active Flutter apps
High Concurrency 10,000 to 25,000 8 vCPU, 16 GB RAM 250 GB Pure NVMe Fast-scaling delivery apps, SaaS platforms, high-volume real-time chat
Enterprise / Scaling 25,000+ Concurrent Bare-Metal Dedicated Server 500 GB+ NVMe RAID High-throughput fintech, nation-wide streaming, live GPS dispatch

Step-by-Step Production Installation on Ubuntu 24.04/22.04 LTS

Follow these steps to deploy a hardened, production-ready Appwrite instance on a clean Ubuntu VPS.

Step 1: Base Operating System Hardening

Log in as the root user over SSH and upgrade all installed packages:

apt-get update && apt-get upgrade -y
apt-get install -y curl wget git ufw apt-transport-https ca-certificates gnupg lsb-release

Configure the Uncomplicated Firewall (UFW) to allow SSH, HTTP, and HTTPS while blocking unauthorized ports:

ufw default deny incoming
ufw default allow outgoing
ufw allow 22/tcp
ufw allow 80/tcp
ufw allow 443/tcp
ufw enable

Step 2: Configure Virtual Memory and Swap Space

MariaDB and the OpenRuntimes executor can experience transient memory spikes during intensive queries or simultaneous function builds. Provision an 8 GB swap file to prevent the Linux kernel Out-Of-Memory (OOM) killer from terminating database processes:

fallocate -l 8G /swapfile
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile

# Persist swap in fstab
echo '/swapfile none swap sw 0 0' >> /etc/fstab

# Optimize swap threshold and inode caching
sysctl vm.swappiness=10
sysctl vm.vfs_cache_pressure=50
echo 'vm.swappiness=10' >> /etc/sysctl.conf
echo 'vm.vfs_cache_pressure=50' >> /etc/sysctl.conf

Step 3: Install Docker Engine and Docker Compose

Install the latest official Docker Community Edition engine and containerd runtime:

install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
chmod a+r /etc/apt/keyrings/docker.gpg

echo \
  "deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
  "$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | \
  tee /etc/apt/sources.list.d/docker.list > /dev/null

apt-get update
apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Verify the installation:

docker --version && docker compose version

Step 4: Provision Appwrite via Docker Compose

Create a dedicated directory to store Appwrite’s operational configuration:

mkdir -p /opt/appwrite
cd /opt/appwrite

Appwrite provides an interactive automated installer that pulls the required images and initializes environment secrets. Execute the official installer:

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

The installer will prompt for key parameters:

  • HTTP Port: Default 80 (press Enter).
  • HTTPS Port: Default 443 (press Enter).
  • Secret API Key: Press Enter to generate a cryptographically secure random string.
  • Hostname / Domain: Enter your fully qualified domain name, such as appwrite.yourdomain.com. Ensure your DNS A-record points directly to your Linux VPS’s static IPv4 address.
  • DNS Target: Enter appwrite.yourdomain.com (press Enter).

Once the installer completes, examine the /opt/appwrite directory. It contains two critical files:

  • docker-compose.yml: Defines the containerized network topology, volumes, and service relationships.
  • .env: Holds all environment variables, credentials, database passwords, and runtime limits.

Production Environment Configuration (.env Tuning)

Default settings are configured for local development. For production deployments in Pakistan, open /opt/appwrite/.env and update the following settings:

nano /opt/appwrite/.env

1. General & Domain Settings

_APP_ENV=production
_APP_DOMAIN=appwrite.yourdomain.com
_APP_DOMAIN_TARGET=appwrite.yourdomain.com
_APP_SYSTEM_EMAIL_NAME="Appwrite Support"
_APP_SYSTEM_EMAIL_ADDRESS="[email protected]"

2. Sizing Workers and System Concurrency

Appwrite allows you to scale worker processes based on the VPS CPU core count. By default, _APP_WORKER_PER_CORE is set to 6. On a 4-core VPS, this spawns 24 instances per worker type, which can oversaturate RAM:

# For a 4-Core VPS: Set to 4 to balance CPU context switching
_APP_WORKER_PER_CORE=4

# Maximum concurrent background task runs
_APP_SYSTEM_TASKS_MAX=100

3. Let’s Encrypt TLS Automation

Ensure Traefik handles certificate issuance without manual intervention:

_APP_SSL_EMAIL[email protected]
_APP_STORAGE_LIMIT=52428800 # 50 MB max file upload size in bytes

4. Restarting the Stack

After applying modifications to .env, restart the Docker Compose stack to apply the changes:

docker compose down
docker compose up -d

Verify that all containers report healthy or running status:

docker compose ps

Database, Cache, and Kernel Performance Tuning

Under heavy production traffic, the default database and Redis configurations can become I/O bottlenecks. Apply the following tuning parameters.

MariaDB InnoDB Buffer Pool Optimization

The single most impactful database tuning parameter is innodb_buffer_pool_size. By default, MariaDB reserves only 128 MB or 256 MB. On a production server, it should be configured to cache 50% to 70% of available memory to eliminate disk read latency.

Create a custom MariaDB override configuration file:

mkdir -p /opt/appwrite/config/mariadb
nano /opt/appwrite/config/mariadb/custom.cnf

Add the following configuration:

[mysqld]
# Sizing for an 8 GB RAM VPS (Allocate 3.5 GB to InnoDB Buffer Pool)
innodb_buffer_pool_size = 3500M
innodb_buffer_pool_instances = 4
innodb_log_file_size = 512M
innodb_log_buffer_size = 64M
innodb_flush_log_at_trx_commit = 2
innodb_flush_method = O_DIRECT

# Connection optimization
max_connections = 500
connect_timeout = 20
wait_timeout = 300
interactive_timeout = 300

# Query limits
table_open_cache = 4000
open_files_limit = 65535

In /opt/appwrite/docker-compose.yml, mount this file under the mariadb container service:

  mariadb:
    image: mariadb:10.11
    volumes:
      - appwrite-mariadb:/var/lib/mysql:rw
      - /opt/appwrite/config/mariadb/custom.cnf:/etc/mysql/conf.d/custom.cnf:ro

Redis Memory and Eviction Limits

Without explicit memory caps, Redis will consume available memory until the host runs out of RAM. Configure Redis to operate within defined boundaries:

mkdir -p /opt/appwrite/config/redis
nano /opt/appwrite/config/redis/redis.conf

Add:

maxmemory 1024mb
maxmemory-policy allkeys-lru
save ""
appendonly no

Mount redis.conf in docker-compose.yml under the redis service:

  redis:
    image: redis:7.0-alpine
    command: ["redis-server", "/usr/local/etc/redis/redis.conf"]
    volumes:
      - appwrite-redis:/data:rw
      - /opt/appwrite/config/redis/redis.conf:/usr/local/etc/redis/redis.conf:ro

Linux Kernel File Descriptor Limits

WebSockets maintain persistent open TCP sockets. If thousands of mobile devices stay connected simultaneously, Linux will throw Too many open files errors unless limits are raised:

nano /etc/security/limits.conf

Append:

* soft nofile 65535
* hard nofile 65535
root soft nofile 65535
root hard nofile 65535

Apply network socket backlog optimizations:

nano /etc/sysctl.d/99-appwrite.conf

Add:

net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535
net.ipv4.ip_local_port_range = 1024 65535
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 15

Load the new sysctl parameters:

sysctl --system

Configuring Production Email Relays & SMS Authentication

Mobile applications rely on transactional communication for sign-up verification, password recovery, and multi-factor authentication (MFA).

Configuring SMTP Relay for Transactional Emails

Using the local VPS sendmail binary often leads to emails landing in spam filters due to residential IP blacklists and missing reverse DNS (rDNS) records. Use a dedicated SMTP relay provider (such as Amazon SES, Postmark, or SendGrid).

Edit /opt/appwrite/.env:

# SMTP Relay Configuration
_APP_SMTP_HOST=email-smtp.us-east-1.amazonaws.com
_APP_SMTP_PORT=587
_APP_SMTP_SECURE=tls
_APP_SMTP_USERNAME=AKIAIOSFODNN7EXAMPLE
_APP_SMTP_PASSWORD=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY

Restart the mail worker container to apply changes:

docker compose up -d --no-deps appwrite-worker-mails

Integrating SMS Phone Authentication for Pakistan Mobile Numbers

Appwrite includes native Phone Authentication with support for Twilio, Vonage, and MessageBird. However, international SMS providers frequently experience delivery failures or high per-message costs when routing to Pakistani mobile networks (Jazz, Telenor, Zong, and Ufone).

To integrate local Pakistani SMS aggregators (such as Brandverse, Veels, SMS4Connect, or customized Telco HTTP APIs), utilize Appwrite’s built-in Serverless Cloud Functions or webhook architecture:

+------------------------------------+
| Appwrite Phone Auth Event Trigger  |
+-----------------+------------------+
                  |
                  v Webhook HTTP POST
+------------------------------------+
| Appwrite Cloud Function (Node.js)  |
| - Generates cryptographically safe |
|   6-digit numeric OTP code         |
| - Encrypts OTP in Appwrite Cache   |
| - Calls Local Telco SMS Gateway    |
+-----------------+------------------+
                  |
                  v HTTPS API Call
+------------------------------------+
| Local Pakistan SMS Aggregator      |
| (Jazz / Zong / Telenor Delivery)   |
+------------------------------------+

Deploying Serverless Cloud Functions (Node.js, Python, Dart)

One of Appwrite’s most powerful capabilities is its integrated Serverless Cloud Functions engine. Functions allow developers to execute backend logic securely without exposing database credentials to client-side Flutter or React Native apps.

How OpenRuntimes Executes Isolated Functions

When a function execution request is triggered:

  1. The Appwrite API validates the client’s session token and permissions.
  2. The appwrite-worker-functions daemon places the job on the Redis execution queue.
  3. The openruntimes-executor container receives the instruction and talks to the host Docker daemon over /var/run/docker.sock.
  4. An isolated micro-container is initialized using the appropriate language runtime image (such as openruntimes/node:v20).
  5. Environment secrets, payload data, and headers are injected via standard I/O.
  6. The function executes, returns its JSON response, and the container instance is recycled or placed back into an execution pool.

Example: Production Node.js Payment Webhook Handler

Below is an Appwrite Cloud Function written in modern Node.js to securely process local Pakistani digital payment callbacks (e.g., EasyPaisa, JazzCash, or Safepay):

// src/index.js
import { Client, Databases } from 'node-appwrite';
import crypto from 'crypto';

export default async ({ req, res, log, error }) => {
  // Verify HTTP Method
  if (req.method !== 'POST') {
    return res.json({ success: false, message: 'Method Not Allowed' }, 405);
  }

  // Validate Secret HMAC Signature from Payment Gateway
  const gatewaySignature = req.headers['x-gateway-signature'];
  const webhookSecret = process.env.PAYMENT_WEBHOOK_SECRET;

  const computedHash = crypto
    .createHmac('sha256', webhookSecret)
    .update(JSON.stringify(req.body))
    .digest('hex');

  if (gatewaySignature !== computedHash) {
    error('Cryptographic signature mismatch! Potential spoofed request.');
    return res.json({ success: false, message: 'Unauthorized signature' }, 401);
  }

  // Initialize Appwrite Server SDK with Admin API Key
  const client = new Client()
    .setEndpoint(process.env.APPWRITE_FUNCTION_API_ENDPOINT)
    .setProject(process.env.APPWRITE_FUNCTION_PROJECT_ID)
    .setKey(process.env.APPWRITE_API_KEY);

  const databases = new Databases(client);

  const { orderId, transactionId, paymentStatus, amountPaid } = req.body;

  try {
    // Update Order Document in Appwrite Database
    const updatedDocument = await databases.updateDocument(
      process.env.DATABASE_ID,
      process.env.ORDERS_COLLECTION_ID,
      orderId,
      {
        status: paymentStatus === 'COMPLETED' ? 'paid' : 'failed',
        transaction_id: transactionId,
        amount: amountPaid,
        updated_at: new Date().toISOString()
      }
    );

    log(`Successfully reconciled order ${orderId} with transaction ${transactionId}`);
    return res.json({ success: true, data: updatedDocument });
  } catch (err) {
    error(`Database update failed: ${err.message}`);
    return res.json({ success: false, error: err.message }, 500);
  }
};

Deploying functions through the Appwrite CLI is straightforward:

# Install Appwrite CLI
npm install -g appwrite-cli

# Authenticate with your self-hosted instance
appwrite login --endpoint https://appwrite.yourdomain.com/v1

# Initialize and deploy
appwrite init function
appwrite deploy function

Realtime WebSockets: Connecting Mobile & Web Clients

Appwrite features a native WebSocket server that streams updates without requiring client-side HTTP polling.

Flutter Implementation Example

Here is how a Flutter application connects to the self-hosted Appwrite backend to listen for live order tracking updates:

import 'package:flutter/material.dart';
import 'package:appwrite/appwrite.dart';

class OrderTrackingService {
  late Client client;
  late Realtime realtime;
  RealtimeSubscription? subscription;

  void initialize() {
    client = Client()
        .setEndpoint('https://appwrite.yourdomain.com/v1')
        .setProject('ecommerce_prod')
        .setSelfSigned(status: false); // Set to true only in dev environments

    realtime = Realtime(client);
  }

  void subscribeToOrder(String databaseId, String collectionId, String orderId) {
    final channel = 'databases.$databaseId.collections.$collectionId.documents.$orderId';

    subscription = realtime.subscribe([channel]);

    subscription!.stream.listen((response) {
      if (response.events.contains('databases.*.collections.*.documents.*.update')) {
        final payload = response.payload;
        debugPrint('Live Order Update Received: ${payload['status']}');
        // Trigger state management update (Bloc / Riverpod / Provider)
      }
    });
  }

  void dispose() {
    subscription?.close();
  }
}

Optimizing WebSocket Performance

  1. Keepalive Pings: Ensure mobile clients establish periodic TCP heartbeat pings (every 30 to 45 seconds) to prevent Pakistani mobile telecommunication carriers (which enforce aggressive 60-second NAT connection timeouts on cellular data) from silently closing idle sockets.
  2. Channel Scoping: Avoid subscribing to entire collections (databases.db.collections.col.documents) on mobile devices. Always narrow subscriptions to specific user document IDs or specific channel filters to minimize downstream bandwidth usage.

Automated Disaster Recovery & Backup Strategy

A production backend must have a reliable disaster recovery plan. Because Appwrite relies on containerized state, backups must capture both relational database records and physical object storage volumes.

Create a robust automated backup script:

mkdir -p /opt/appwrite/scripts
nano /opt/appwrite/scripts/backup.sh

Add the following production bash script:

#!/usr/bin/env bash
set -eo pipefail

BACKUP_ROOT="/var/backups/appwrite"
TIMESTAMP=$(date +"%Y%m%d_%H%M%S")
DEST_DIR="${BACKUP_ROOT}/${TIMESTAMP}"
RETENTION_DAYS=7

mkdir -p "${DEST_DIR}"

echo "[${TIMESTAMP}] Starting Appwrite automated disaster recovery backup..."

# 1. Export MariaDB Relational Database
echo "Exporting MariaDB database..."
docker exec appwrite-mariadb mariadb-dump \
  -u root \
  --password="$(grep _APP_DB_ROOT_PASS /opt/appwrite/.env | cut -d '=' -f2)" \
  --all-databases \
  --single-transaction \
  --quick \
  --lock-tables=false | gzip > "${DEST_DIR}/mariadb_dump_${TIMESTAMP}.sql.gz"

# 2. Archive Appwrite Uploaded Storage Files
echo "Archiving object storage uploads..."
tar -czf "${DEST_DIR}/appwrite_uploads_${TIMESTAMP}.tar.gz" \
  -C /var/lib/docker/volumes/appwrite_appwrite-uploads/_data .

# 3. Archive Configuration & Encryption Keys
echo "Archiving .env and docker-compose.yml..."
tar -czf "${DEST_DIR}/appwrite_configs_${TIMESTAMP}.tar.gz" \
  -C /opt/appwrite .env docker-compose.yml

echo "[${TIMESTAMP}] Local backup successfully generated at ${DEST_DIR}"

# 4. Prune Backups Older than Retention Window
find "${BACKUP_ROOT}" -mindepth 1 -maxdepth 1 -type d -mtime +${RETENTION_DAYS} -exec rm -rf {} +
echo "Pruned local backup archives older than ${RETENTION_DAYS} days."

# Optional: Push backup offsite to S3-compatible cold storage
# rclone copy "${DEST_DIR}" remote:appwrite-backups-pakistan/${TIMESTAMP}

Make the script executable and schedule it in crontab to run daily at 03:00 AM PKT:

chmod +x /opt/appwrite/scripts/backup.sh

(crontab -l 2>/dev/null; echo "0 3 * * * /opt/appwrite/scripts/backup.sh >> /var/log/appwrite_backup.log 2>&1") | crontab -

When to Scale: Transitioning from VPS to Bare-Metal Dedicated Servers

A high-performance Linux VPS is ideal for launching mobile apps, hosting internal business tools, and handling moderate production loads. However, as your user base expands into millions of registered users or tens of thousands of simultaneous real-time connections, shared virtualization presents physical limitations.

+-----------------------------------------------------------------------------------+
| Hypervisor Virtualization Limits                                                  |
| - Shared CPU Core Schedulers (CPU Steal 'st%' spikes during neighbor bursts)      |
| - Shared Disk I/O Buses (Random I/O wait 'wa%' during peak hours)                 |
| - Virtual Network Switch overhead on 25,000+ concurrent WebSockets                |
+-----------------------------------------+-----------------------------------------+
                                          |
                                          v [ Infrastructure Scale Threshold ]
+-----------------------------------------------------------------------------------+
| Bare-Metal Dedicated Server Infrastructure                                        |
| - 100% Dedicated Physical Cores (AMD EPYC / Intel Xeon) with zero CPU steal       |
| - Direct PCIe Gen4 NVMe RAID arrays (1,000,000+ random read/write IOPS)           |
| - Massive 64 GB to 512 GB DDR5 RAM for MariaDB buffer pools and Redis queues      |
| - Unmetered high-throughput gigabit network connectivity                          |
+-----------------------------------------------------------------------------------+

Critical Indicators You Have Outgrown Shared VPS Infrastructure

  1. CPU Steal Contention (st% in top): In shared multi-tenant virtualization, host servers multiplex virtual cores across multiple clients. During traffic surges, your VPS may experience CPU steal (st% exceeding 5% to 10%), causing WebSocket handshakes to lag and background worker tasks to queue up.
  2. I/O Wait Bottlenecks (wa%): As the MariaDB database exceeds dozens of gigabytes and serverless functions write thousands of logs per minute, disk I/O operations will bottleneck. If I/O wait (wa%) regularly exceeds 15%, the database cannot write fast enough to keep up with incoming Redis queue items.
  3. Massive WebSocket Concurrency (25,000+ Active Clients): Handling tens of thousands of active socket connections requires substantial memory and uninterrupted kernel network interrupt processing. Hypervisors introduce micro-latencies that can cause mass connection dropouts and reconnection thundering herds.
  4. Heavy Multi-Runtime Serverless Execution: When your application triggers hundreds of simultaneous cloud functions per minute, Docker container spin-up overhead places continuous load on the host kernel’s cgroups and namespaces.

When your application reaches these operational limits, migrating your Appwrite backend to high-throughput, unmetered Dedicated Servers provides dedicated physical CPU hardware, dedicated PCIe NVMe drives, and zero neighbor contention.

For engineering teams serving users across Pakistan, deploying on enterprise-grade Dedicated Servers in Pakistan ensures ultra-low ping times to local mobile operators (Jazz, Zong, Telenor, Ufone), compliance with data localization mandates, and dedicated enterprise hardware.


Production Security & Deployment Checklist

Before launching your mobile application or web portal on a self-hosted Appwrite instance, verify this operational checklist:

  • Linux VPS provisioned with Ubuntu 24.04/22.04 LTS and pure NVMe storage.
  • 8 GB swapfile configured with vm.swappiness=10 to protect MariaDB against OOM termination.
  • Docker Engine and Docker Compose installed via official Docker repositories.
  • UFW firewall enabled, restricting access to ports 22, 80, and 443 only.
  • Domain A-record configured and verified before launching the Appwrite installer.
  • Let’s Encrypt automated TLS certificate issuance verified through Traefik.
  • _APP_WORKER_PER_CORE adjusted to match available VPS CPU core count.
  • MariaDB innodb_buffer_pool_size tuned to 50%-70% of database memory allocation.
  • Redis maxmemory and maxmemory-policy allkeys-lru configured in redis.conf.
  • Linux system limits raised (nofile 65535 and net.core.somaxconn 65535).
  • Dedicated SMTP relay configured in .env and email verification delivery tested.
  • Serverless functions secured with HMAC signatures and API keys stored as function secrets.
  • Automated disaster recovery backup script verified, tested with dry-run restore, and scheduled via cron.

PRODUCTION LINUX VPS INFRASTRUCTURE

Deploy Self-Hosted Appwrite on Pure NVMe Linux VPS

Eliminate recurring USD SaaS overhead, billing shocks, and foreign exchange limits. Launch high-performance, low-latency Docker-ready Linux VPS or Bare-Metal Dedicated Servers in Pakistan with local billing and 24/7 engineering support.