bolt Valebyte VPS from $4/mo — NVMe, 60s deploy.

Get a VPS arrow_forward
eco Beginner Tutorial/How-to

Deploy AppFlowy on Your

calendar_month Aug 08, 2026 schedule 22 min read visibility 36 views
Развёртывание AppFlowy на VPS: Свой аналог Notion с Docker и PostgreSQL
info

Need a server for this guide? We offer dedicated servers and VPS in 50+ countries with instant setup.

Need a server for this guide?

Deploy a VPS or dedicated server in minutes.

Deploying AppFlowy on a VPS: Your Own Notion Alternative with Docker and PostgreSQL

TL;DR

In this detailed guide, we will step-by-step set up AppFlowy — a powerful open-source alternative to Notion — on your own VPS. You will learn how to install and configure Docker, PostgreSQL, AppFlowy Server, and also ensure secure access via HTTPS using Caddy. Upon completing the tutorial, you will have a fully functional and secure tool for knowledge and project management that is under your complete control.

  • Installing AppFlowy Server using Docker Compose for easy deployment.
  • Setting up a dedicated PostgreSQL database for AppFlowy data storage.
  • Ensuring secure access to AppFlowy via HTTPS with automatic SSL certificate management by Caddy.
  • Basic VPS preparation: configuring security, firewall, and SSH access.
  • Recommendations for data backup and system maintenance for long-term stability.

What we configure and why

Diagram: What we configure and why
Diagram: What we configure and why

In the modern world, effective information and project management is key to success for both individual specialists and teams. Notion has become a de facto standard for many due to its flexibility and versatility. However, one doesn't always want to entrust all their data to third-party cloud services. This is where AppFlowy comes to the rescue — a powerful, fully open-source alternative to Notion that can be deployed on your own server.

AppFlowy offers a similar user experience with blocks, databases, Kanban boards, and wiki pages, but provides full control over your data. It's an ideal solution for those who value privacy, want to customize the system to their unique needs, or simply prefer to own their infrastructure rather than rent it.

In this guide, we will deploy AppFlowy Server on a Virtual Private Server (VPS) using containerization with Docker and Docker Compose. PostgreSQL will be used as the database, ensuring reliable and scalable data storage. We will also configure automatic retrieval and renewal of SSL certificates using Caddy, so your AppFlowy is accessible via a secure HTTPS connection.

Ultimately, you will get a fully functional, secure, and self-controlled Notion alternative, accessible to your team or for personal use via a web browser. This will allow you to store confidential data on your own infrastructure, minimizing risks associated with reliance on third-party providers.

Alternatives: Cloud-managed vs. Self-hosted

Before diving into the setup, it's important to understand why a self-hosted solution, such as AppFlowy on a VPS, might be preferable to cloud alternatives:

  • Cloud-managed services (e.g., Notion, Monday.com, ClickUp):
    • Pros: Easy to start, no technical expertise required, automatic scaling and backups, provider support.
    • Cons: High cost as the team grows, lack of full data control, reliance on provider's privacy policy, limited customization options.
  • Self-hosted on VPS (e.g., AppFlowy, GitLab Community Edition, Mattermost):
    • Pros: Full control over data and security, potentially lower long-term cost (especially for larger teams), high degree of customization, ability to integrate with your own infrastructure.
    • Cons: Requires technical knowledge for installation and maintenance, responsibility for backups and updates lies with you, initial setup can be time-consuming.

Choosing self-hosted AppFlowy on a VPS is ideal for those willing to invest time in setup for complete independence and control, or for teams with sensitive data that cannot be hosted on third-party servers.

What VPS configuration is needed for this task

Diagram: What VPS configuration is needed for this task
Diagram: What VPS configuration is needed for this task

Choosing the right VPS configuration is critical for stable and fast AppFlowy operation. While AppFlowy itself is not an extremely resource-intensive application, working with Docker, PostgreSQL, and a web server requires a certain reserve of resources. The requirements below are current for 2026 and are designed for stable operation for a small team (up to 10-15 active users).

Minimum Requirements

  • CPU: 2 cores. Modern VPS processors provide sufficient per-core performance.
  • RAM: 4 GB. Of this, 2 GB will be used by Docker containers (AppFlowy Server, PostgreSQL, Caddy) and another 2 GB for the operating system, caching, and potential growth.
  • Disk: 80 GB NVMe SSD. A fast SSD significantly improves database performance. 80 GB is sufficient for the OS, Docker images, AppFlowy data, and several weeks of backups. For long-term backup storage, external storage is recommended.
  • Network: 100 Mbps or 1 Gbps port. For a web application like AppFlowy, stability is more important than peak bandwidth, unless you plan to transfer very large files.

Recommended VPS Plan

For comfortable operation and the possibility of minor scaling in the future, as well as to ensure stability during peak loads, the following characteristics are recommended:

  • CPU: 4 cores.
  • RAM: 8 GB.
  • Disk: 160 GB NVMe SSD.
  • Network: 1 Gbps port.

Such a configuration will ensure smooth AppFlowy operation even with several users working simultaneously, active database usage, and content indexing. You can consider a VPS with the specified characteristics for rent. Ensure that the provider offers reliable infrastructure and access to current operating system versions, such as Ubuntu Server 24.04 LTS.

When a Dedicated Server is Needed, Not a VPS

A dedicated server becomes necessary when:

  • Large number of users: If you plan deployment for more than 50 active users or for corporate use with critically important data.
  • High performance requirements: For very intensive workloads, such as processing large volumes of data, machine learning, or high-load databases, where even the most powerful VPS may be insufficient.
  • Strict security and compliance requirements: Some regulatory requirements may mandate the use of physically isolated hardware.
  • Hardware customization: The need for specific RAID configurations, GPUs, or unusual network cards.

For most AppFlowy deployments for personal use or a small/medium team, a well-configured VPS will be more than sufficient.

Location: What it affects

The choice of the geographical location of your VPS server affects several key aspects:

  • Latency: The closer the server is to your users, the lower the latency and faster the application's response. For a team distributed across different regions, a central location should be chosen.
  • Legislation: Data protection laws (e.g., GDPR in the EU) may impose restrictions on where data can be stored. Choose a location that complies with your legal requirements.
  • Cost: VPS prices can vary significantly by region due to the cost of electricity, real estate, and network infrastructure.
  • Availability: Some regions may have better connectivity to global internet backbones, providing a more stable connection.

For AppFlowy, it is generally sufficient to choose a server in the country where most of your users are located to minimize latency.

Server Preparation

Diagram: Server Preparation
Diagram: Server Preparation

Before proceeding with the AppFlowy installation, you need to perform basic setup and security hardening for your new VPS. We will use Ubuntu Server 24.04 LTS as the primary operating system, as it is one of the most popular and stable platforms for server deployments.

1. SSH Connection

First, connect to your VPS as the root user, using the IP address provided by your provider:


ssh root@YOUR_VPS_IP_ADDRESS

If this is your first connection, you may need to accept the server's key fingerprint. Enter the root password if prompted.

2. Creating a New User with Sudo Privileges

Working as the root user is insecure. Let's create a new user for everyday tasks and grant them sudo privileges.


# Replace 'appflowyuser' with your desired username
adduser appflowyuser

Follow the instructions: enter and confirm the password, then you can skip filling in additional information by simply pressing Enter.

Now, add the new user to the sudo group so they can execute commands with administrator privileges:


usermod -aG sudo appflowyuser

3. Setting up SSH Key Authentication (Recommended)

Using SSH keys significantly enhances security compared to password authentication. If you don't already have an SSH key pair, generate them on your local machine:


# On your LOCAL machine
ssh-keygen -t rsa -b 4096

Then, copy the public key to your VPS for the new user:


# On your LOCAL machine, replace 'appflowyuser' and 'YOUR_VPS_IP_ADDRESS'
ssh-copy-id appflowyuser@YOUR_VPS_IP_ADDRESS

After this, exit the root session and log in as the new user using the SSH key:


# On your LOCAL machine
exit
ssh appflowyuser@YOUR_VPS_IP_ADDRESS

If you can log in without a password, the keys are configured correctly. Now, let's disable password authentication for SSH and disallow root login.


# On the VPS, as 'appflowyuser'
sudo nano /etc/ssh/sshd_config

Find and modify the following lines (add them if they don't exist):


# ...
PermitRootLogin no
PasswordAuthentication no
ChallengeResponseAuthentication no
UsePAM yes
# ...

Save changes (Ctrl+O, Enter) and exit (Ctrl+X). Then restart the SSH service:


sudo systemctl restart sshd

IMPORTANT: Make sure you can log in as the new user with an SSH key BEFORE disabling password authentication and root login. Otherwise, you risk losing access to the server.

4. System Update

Always start by updating the package database and installed packages to their latest versions.


# Update package list
sudo apt update
# Upgrade all installed packages
sudo apt upgrade -y
# Remove old, unnecessary packages
sudo apt autoremove -y

5. Firewall Configuration (UFW)

UFW (Uncomplicated Firewall) is an easy-to-use interface for configuring iptables rules. Let's enable it and allow only the necessary ports.


# Allow SSH (port 22)
sudo ufw allow OpenSSH
# Allow HTTP (port 80)
sudo ufw allow http
# Allow HTTPS (port 443)
sudo ufw allow https
# Enable firewall
sudo ufw enable
# Check firewall status
sudo ufw status

Ensure that the status is active and ports 80, 443, 22 are allowed. Allowing OpenSSH before enabling UFW is critical to avoid losing access to the server.

6. Installing Fail2Ban

Fail2Ban helps protect the server from brute-force attacks by blocking IP addresses that exhibit multiple failed login attempts.


# Install Fail2Ban
sudo apt install fail2ban -y
# Start and enable autostart
sudo systemctl enable fail2ban
sudo systemctl start fail2ban
# Check status (optional)
sudo systemctl status fail2ban

Fail2Ban protects SSH by default. For additional services, you can configure rules in the /etc/fail2ban/jail.local file.

Your server is now ready for the installation of core software.

Software Installation — Step-by-Step

Diagram: Software Installation — Step-by-Step
Diagram: Software Installation — Step-by-Step

In this step, we will install all necessary software: Docker and Docker Compose for containerizing AppFlowy Server and PostgreSQL, as well as Caddy for the web server and automatic SSL certificate acquisition.

1. Installing Docker Engine

Docker is a platform for developing, delivering, and running applications in containers. We will use the official Docker repository to get the latest version, current for 2026 (assuming Docker Engine v25.x or v26.x).


# Update package list
sudo apt update
# Install necessary packages for working with HTTPS repositories
sudo apt install ca-certificates curl gnupg -y
# Create directory for GPG keys
sudo install -m 0755 -d /etc/apt/keyrings
# Download official Docker GPG key
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
# Change file permissions for the key
sudo chmod a+r /etc/apt/keyrings/docker.gpg
# Add Docker repository to APT sources list
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" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# Update package list with the new Docker repository
sudo apt update
# Install Docker Engine, Docker CLI, and containerd
sudo apt install docker-ce docker-ce-cli containerd.io -y

Add your user to the docker group to execute Docker commands without sudo:


# Replace 'appflowyuser' with your username
sudo usermod -aG docker appflowyuser
# Log out and log back into the SSH session for changes to take effect
exit
ssh appflowyuser@YOUR_VPS_IP_ADDRESS

Verify that Docker is installed correctly:


# Check Docker version
docker --version
# Check Docker operation by running a test container
docker run hello-world

The hello-world output will confirm a successful installation.

2. Installing Docker Compose

Docker Compose allows you to define and run multi-container Docker applications. We will install it from the official repository, ensuring it's up-to-date for 2026 (assuming Docker Compose v2.25.x or newer).


# Install Docker Compose
sudo apt install docker-compose-plugin -y

Check the Docker Compose version:


# Check Docker Compose version
docker compose version

3. Installing Caddy

Caddy is a powerful, extensible web server with automatic HTTPS. It is easy to configure and ideal for providing secure access to AppFlowy.


# Install necessary packages
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
# Download Caddy GPG key
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
# Add Caddy repository
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
# Update package list
sudo apt update
# Install Caddy
sudo apt install caddy -y

Check Caddy's status:


# Check Caddy service status
sudo systemctl status caddy

It should be active and running. If not, start it: sudo systemctl start caddy.

4. Domain Name and DNS Configuration

For HTTPS to work with Caddy, you will need a domain name (e.g., appflowy.yourdomain.com) pointing to your VPS's IP address. Go to your domain registrar's or DNS provider's control panel and create an A record:

  • Type: A
  • Name/Host: appflowy (or another subdomain you wish to use)
  • Value: Your_VPS_IP_Address
  • TTL: 300 (or less for faster updates)

Wait a few minutes (up to several hours, depending on TTL) for DNS records to propagate. You can check this using the dig command (on your local machine):


# On your LOCAL machine
dig appflowy.yourdomain.com

The response should contain an ANSWER SECTION with your VPS's IP address.

Now all necessary components are installed and ready for further configuration.

Configuration

Diagram: Configuration
Diagram: Configuration

In this step, we will configure AppFlowy Server, PostgreSQL, and Caddy to work together. We will use Docker Compose to orchestrate all services.

1. Creating project directories

Let's create the root directory for our AppFlowy project and subdirectories to store PostgreSQL data and Caddy configuration.


# Create the main project directory
mkdir -p ~/appflowy-server
cd ~/appflowy-server
# Create directory for PostgreSQL data
mkdir -p ./data/postgresql
# Create directory for Caddy configuration
mkdir -p ./caddy/Caddyfile
mkdir -p ./caddy/data # Caddy will store certificates here
mkdir -p ./caddy/config # Caddy will store its configuration here

2. Creating the .env file for environment variables

To manage secrets and configuration parameters, such as database passwords, we will use an .env file. This is safer than hardcoding them in the docker-compose.yml file.


nano .env

Add the following content, changing the values of YOUR_DB_PASSWORD, YOUR_APPFLOWY_SECRET_KEY, and YOUR_APPFLOWY_JWT_SECRET to strong, randomly generated strings. Use a password generator for this.


# PostgreSQL Database Configuration
POSTGRES_DB=appflowy_db
POSTGRES_USER=appflowy_user
POSTGRES_PASSWORD=YOUR_DB_PASSWORD # Generate a strong password

# AppFlowy Server Configuration
APPFLOWY_SERVER_PORT=3060 # Default port for AppFlowy server
APPFLOWY_SERVER_HOST=0.0.0.0 # Listen on all interfaces
APPFLOWY_SERVER_SECRET_KEY=YOUR_APPFLOWY_SECRET_KEY # Generate a strong, random key
APPFLOWY_SERVER_JWT_SECRET=YOUR_APPFLOWY_JWT_SECRET # Generate a strong, random key for JWT
APPFLOWY_SERVER_DATABASE_URL=postgresql://appflowy_user:YOUR_DB_PASSWORD@db:5432/appflowy_db

# Optional: Set log level for AppFlowy server (e.g., info, debug, error)
# APPFLOWY_SERVER_LOG_LEVEL=info

# Caddy Configuration (for external access)
CADDY_DOMAIN=appflowy.yourdomain.com # Replace with your actual domain

Save the file (Ctrl+O, Enter) and exit (Ctrl+X).

3. Creating the docker-compose.yml file

This file will define three services: PostgreSQL, AppFlowy Server, and Caddy.


nano docker-compose.yml

Paste the following content. Make sure the image versions are up-to-date for 2026 (AppFlowy Server v0.4.0, PostgreSQL v16.x, Caddy v2.x).


version: '3.8'

services:
  db:
    image: postgres:16-alpine # Using the current PostgreSQL version for 2026
    restart: unless-stopped
    env_file:
      - .env
    environment:
      POSTGRES_DB: ${POSTGRES_DB}
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    volumes:
      - ./data/postgresql:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
      interval: 10s
      timeout: 5s
      retries: 5

  appflowy:
    image: appflowy/appflowy-server:0.4.0 # Assumed current AppFlowy Server version for 2026
    restart: unless-stopped
    env_file:
      - .env
    environment:
      AF_SERVER_HOST: ${APPFLOWY_SERVER_HOST}
      AF_SERVER_PORT: ${APPFLOWY_SERVER_PORT}
      AF_SERVER_SECRET_KEY: ${APPFLOWY_SERVER_SECRET_KEY}
      AF_SERVER_JWT_SECRET: ${APPFLOWY_SERVER_JWT_SECRET}
      AF_SERVER_DATABASE_URL: ${APPFLOWY_SERVER_DATABASE_URL}
      # AF_SERVER_LOG_LEVEL: ${APPFLOWY_SERVER_LOG_LEVEL} # Uncomment to change logging level
    ports:
      - "127.0.0.1:${APPFLOWY_SERVER_PORT}:${APPFLOWY_SERVER_PORT}" # Open port only for localhost, Caddy will proxy
    depends_on:
      db:
        condition: service_healthy # AppFlowy starts only after DB is ready
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:${APPFLOWY_SERVER_PORT}/health"]
      interval: 30s
      timeout: 10s
      retries: 5

  caddy:
    image: caddy:2.7.6-alpine # Current Caddy version for 2026
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./caddy/Caddyfile:/etc/caddy/Caddyfile # Your Caddyfile
      - ./caddy/data:/data # For storing TLS certificates
      - ./caddy/config:/config # For storing Caddy's configuration
    environment:
      CADDY_DOMAIN: ${CADDY_DOMAIN} # Pass domain from .env
    depends_on:
      appflowy:
        condition: service_healthy # Caddy starts only after AppFlowy is ready

Save the file.

4. Configuring the Caddyfile

Now let's create a configuration file for Caddy that will proxy requests to the AppFlowy Server and automatically manage SSL certificates.


nano ./caddy/Caddyfile

Paste the following content. Make sure {$CADDY_DOMAIN} will be replaced with your domain name from the .env file.


{$CADDY_DOMAIN} {
    # Enable automatic HTTPS
    tls {
        dns cloudflare {env.CLOUDFLARE_API_TOKEN} # If using Cloudflare DNS, otherwise remove or use HTTP-01
    }

    # Proxy all requests to AppFlowy Server
    reverse_proxy appflowy:3060 {
        # Additional proxy settings if required
        header_up Host {host}
        header_up X-Real-IP {remote_ip}
        header_up X-Forwarded-For {remote_ip}
        header_up X-Forwarded-Proto {scheme}
    }

    # Enable data compression for faster loading
    encode gzip zstd

    # Logging
    log {
        output file /var/log/caddy/access.log
    }
}

Important note on TLS: The example above uses Cloudflare DNS provider to issue Let's Encrypt certificates (dns cloudflare). This is convenient if your DNS is managed through Cloudflare, and you don't need to open ports 80/443 on Caddy for ACME HTTP-01 challenge (although we have already opened them). If you are not using Cloudflare or prefer HTTP-01 verification (which works by default if 80/443 are open), simply remove the line dns cloudflare {env.CLOUDFLARE_API_TOKEN}. Caddy will automatically attempt to use HTTP-01. If you are using a DNS provider other than Cloudflare, Caddy supports many plugins for various DNS providers. You may need to build Caddy with the appropriate plugin or use HTTP-01.

If you decide to use the DNS challenge with Cloudflare, you will also need to add the CLOUDFLARE_API_TOKEN environment variable to your .env file. Create an API token in Cloudflare with permissions to edit DNS zones for your domain.


# ... (at the end of your .env file)
CLOUDFLARE_API_TOKEN=YOUR_CLOUDFLARE_API_TOKEN # Only if using DNS challenge with Cloudflare

Save the Caddyfile.

5. Starting AppFlowy with Docker Compose

Now that all configuration files are ready, you can start all services.


# Make sure you are in the ~/appflowy-server directory
cd ~/appflowy-server

# Start all services in the background
docker compose up -d

This will download the necessary Docker images (if not present), create containers, set up networks, and start AppFlowy Server, PostgreSQL, and Caddy.

6. Checking operability

Give the services a few minutes to start and initialize. You can monitor the logs:


# View logs of all services
docker compose logs -f

Look for messages indicating successful startup of AppFlowy Server and Caddy. If Caddy reports errors, check your Caddyfile and DNS settings for correctness.

After the logs show that all services are running without errors, check the container status:


docker compose ps

All containers should have a status of running (healthy).

Finally, open your web browser and navigate to your domain (e.g., https://appflowy.yourdomain.com). You should see the AppFlowy registration/login page. Register the first user, and you will gain access to your own AppFlowy Workspace.

Backups and Maintenance

Diagram: Backups and Maintenance
Diagram: Backups and Maintenance

Regular backups and system maintenance are key to the long-term stability and security of your AppFlowy Server. Do not neglect this step.

1. What to Back Up

For a full AppFlowy recovery, you will need to save the following data:

  • PostgreSQL Database: Contains all your projects, pages, blocks, users, and their data. This is the most critical component.
  • AppFlowy Configuration Files: Although most of the configuration is stored in .env and docker-compose.yml, there might be other important files (though for AppFlowy Server, this is less critical than for other applications).
  • Caddy Data: Primarily, these are the SSL certificates that Caddy automatically obtains and stores. They are located in the directory ./caddy/data.
  • Docker Compose Files: The docker-compose.yml and .env files, which define your infrastructure.

2. Simple Auto-Backup Script

Let's create a simple script that will back up the PostgreSQL database and key files, and then archive them.


# Create a directory for scripts
mkdir -p ~/scripts
nano ~/scripts/backup_appflowy.sh

Insert the following content:


#!/bin/bash

# --- Configuration ---
BACKUP_DIR="/var/backups/appflowy" # Directory for storing backups
TIMESTAMP=$(date +"%Y%m%d%H%M%S")
APPFLOWY_PROJECT_DIR="/home/appflowyuser/appflowy-server" # Path to your AppFlowy directory
DB_CONTAINER_NAME="appflowy-server_db_1" # Database container name (may vary)
DB_NAME=$(grep POSTGRES_DB ${APPFLOWY_PROJECT_DIR}/.env | cut -d '=' -f2)
DB_USER=$(grep POSTGRES_USER ${APPFLOWY_PROJECT_DIR}/.env | cut -d '=' -f2)

# --- Create backup directory if it doesn't exist ---
mkdir -p ${BACKUP_DIR}

echo "--- Starting AppFlowy backup ---"

# --- 1. PostgreSQL Database Backup ---
echo "Creating PostgreSQL dump..."
docker exec ${DB_CONTAINER_NAME} pg_dump -U ${DB_USER} -d ${DB_NAME} > ${BACKUP_DIR}/appflowy_db_backup_${TIMESTAMP}.sql
if [ $? -eq 0 ]; then
    echo "PostgreSQL dump successfully created: ${BACKUP_DIR}/appflowy_db_backup_${TIMESTAMP}.sql"
else
    echo "Error creating PostgreSQL dump. Check logs."
    exit 1
fi

# --- 2. Copying configuration files and certificates ---
echo "Copying configuration files and certificates..."
cp ${APPFLOWY_PROJECT_DIR}/docker-compose.yml ${BACKUP_DIR}/
cp ${APPFLOWY_PROJECT_DIR}/.env ${BACKUP_DIR}/
cp ${APPFLOWY_PROJECT_DIR}/caddy/Caddyfile ${BACKUP_DIR}/
# Copying Caddy data (certificates)
cp -r ${APPFLOWY_PROJECT_DIR}/caddy/data ${BACKUP_DIR}/caddy_data_${TIMESTAMP}
cp -r ${APPFLOWY_PROJECT_DIR}/caddy/config ${BACKUP_DIR}/caddy_config_${TIMESTAMP}

# --- 3. Archiving all backup files ---
echo "Archiving backup..."
tar -czf ${BACKUP_DIR}/appflowy_backup_full_${TIMESTAMP}.tar.gz \
    -C ${BACKUP_DIR} \
    appflowy_db_backup_${TIMESTAMP}.sql \
    docker-compose.yml \
    .env \
    Caddyfile \
    caddy_data_${TIMESTAMP} \
    caddy_config_${TIMESTAMP}

if [ $? -eq 0 ]; then
    echo "Full backup archive created: ${BACKUP_DIR}/appflowy_backup_full_${TIMESTAMP}.tar.gz"
    # Deleting temporary files
    rm ${BACKUP_DIR}/appflowy_db_backup_${TIMESTAMP}.sql
    rm ${BACKUP_DIR}/docker-compose.yml
    rm ${BACKUP_DIR}/.env
    rm ${BACKUP_DIR}/Caddyfile
    rm -rf ${BACKUP_DIR}/caddy_data_${TIMESTAMP}
    rm -rf ${BACKUP_DIR}/caddy_config_${TIMESTAMP}
else
    echo "Error archiving backup."
    exit 1
fi

# --- 4. Deleting old backups (keep last 7 days) ---
echo "Deleting old backups..."
find ${BACKUP_DIR} -type f -name "appflowy_backup_full_*.tar.gz" -mtime +7 -delete
echo "--- Backup completed ---"

Make the script executable:


chmod +x ~/scripts/backup_appflowy.sh

Check the PostgreSQL container name using docker compose ps in the ~/appflowy-server directory. It usually looks like appflowy-server_db_1, but may vary. Update the DB_CONTAINER_NAME variable in the script if necessary.

3. Setting Up Cron for Automatic Backup

To make the script run automatically, let's add it to the Cron schedule.


crontab -e

Choose a text editor if prompted. Add the following line to the end of the file to make the script run daily at 03:00 AM:


0 3 * * * /home/appflowyuser/scripts/backup_appflowy.sh >> /var/log/appflowy_backup.log 2>&1

Save and exit. Backups will now be created automatically.

4. Where to Store Backups

Storing backups on the same server as the main service is extremely risky. If the server fails, you will lose both the service and the backups. Recommended options:

  • External S3-compatible storage: Services like AWS S3, Backblaze B2, DigitalOcean Spaces, or any other object storage providers. You can modify the backup script to automatically upload archives to S3 using aws cli or rclone.
  • Separate VPS or NAS: If you have another server or network-attached storage, you can configure rsync or scp to transfer archived backups there.

Example of using rclone for S3 (requires rclone installation and configuration):


# Inside backup_appflowy.sh, after creating the archive:
# rclone copy ${BACKUP_DIR}/appflowy_backup_full_${TIMESTAMP}.tar.gz remote_s3_bucket:appflowy-backups/

5. Updates: Rolling vs. Maintenance Window

Regular updates are critically important for security and gaining new features.

  • OS Updates: Recommended to perform once a month. This can be done manually or by configuring automatic security updates.
    
    sudo apt update && sudo apt upgrade -y && sudo apt autoremove -y
    sudo reboot # If a kernel or other critical component update is required
                
  • Docker Image Updates (AppFlowy, PostgreSQL, Caddy):
    • Maintenance Window: The preferred approach. Schedule a time when AppFlowy is not actively in use.
      
      cd ~/appflowy-server
      docker compose pull # Download new image versions
      docker compose up -d # Recreate containers with new images
                          
      This will ensure minimal downtime and allow you to verify functionality after the update.
    • Rolling Updates (for more complex systems): Not directly applicable to our current Docker Compose file without additional orchestration tools (e.g., Kubernetes). For a single server, it's always better to use a maintenance window.

Always perform a full backup before every major update!

Troubleshooting + FAQ

This section provides answers to frequently asked questions and solutions to common problems that may arise during the deployment and operation of AppFlowy on a VPS.

Cannot connect via SSH after configuring firewall or SSH keys. What to do?

Error: SSH connection rejected, or timeout. What to check:

  1. Ensure that you allowed the SSH port (default 22) in UFW using sudo ufw allow OpenSSH before enabling the firewall.
  2. If you disabled password authentication and root login, ensure that your public SSH key is correctly copied to the server for your user (~/.ssh/authorized_keys) and has the correct permissions (chmod 600 ~/.ssh/authorized_keys).
  3. Check that the SSH service (sshd) is running: sudo systemctl status sshd.
How to fix: If you have lost access, you may need to use your VPS provider's recovery console to log in and correct SSH or UFW settings. Be extremely careful when changing SSH and UFW settings.

AppFlowy is inaccessible via domain name, but containers are running.

Error: The web browser shows "Site unreachable" or "Connection refused". What to check:

  1. DNS records: Ensure that your A-record for the domain (e.g., appflowy.yourdomain.com) correctly points to your VPS's IP address. Use dig appflowy.yourdomain.com (on your local PC).
  2. VPS Firewall: Check that ports 80 and 443 are allowed in UFW: sudo ufw status.
  3. Caddy service: Ensure that Caddy is running and has no errors: sudo systemctl status caddy and docker compose logs caddy. Check Caddyfile for syntax errors.
  4. TLS Certificates: Caddy should automatically obtain certificates. If there are errors, check Caddy logs. Ensure that ports 80 and 443 are accessible from the internet for HTTP-01 verification, or that the Caddy DNS plugin is configured correctly if you are using it.
  5. AppFlowy availability inside Docker: Ensure that the AppFlowy container is healthy: docker compose ps. Check its logs: docker compose logs appflowy.
How to fix: Correct DNS, open ports in UFW, fix Caddyfile, or restart Caddy/Docker Compose.

What is the minimum suitable VPS configuration?

For AppFlowy Server on Docker with PostgreSQL, a minimum of 2 CPU cores, 4 GB RAM, and 80 GB NVMe SSD is recommended. This will be sufficient for personal use or a small team of up to 5 people. However, for more comfortable operation and growth potential, it's better to aim for 4 CPU cores, 8 GB RAM, and 160 GB NVMe SSD. This will provide better database performance and overall system responsiveness.

What to choose — VPS or dedicated for this task?

For deploying AppFlowy for personal needs or a small/medium team (up to 50 users), a VPS is the optimal choice. It offers an excellent balance between cost, flexibility, and performance. A dedicated server is recommended only for very large teams, corporate environments with high performance and security requirements, or in cases where specific hardware configuration is needed. For most AppFlowy users, a VPS will be more than sufficient.

PostgreSQL container does not start or is unhealthy.

Error: docker compose ps shows unhealthy or exited for the db container. What to check:

  1. Container logs: docker compose logs db. Look for errors related to PostgreSQL startup, data access permissions, or port conflicts.
  2. Data directory permissions: Ensure that the ./data/postgresql directory has the correct permissions for the Docker user. By default, Docker manages this, but if you manually changed permissions, problems may arise.
  3. Password: Check that POSTGRES_PASSWORD in .env meets PostgreSQL requirements and does not contain special characters that might be misinterpreted.
How to fix: Correct errors in the logs. Sometimes it helps to remove Docker volumes (if there is no important data) and restart: docker compose down -v (be careful, this will delete DB data!) and then docker compose up -d.

How to update AppFlowy Server to a new version?

Steps:

  1. Perform a backup: Always perform a full backup before updating.
  2. Stop services: cd ~/appflowy-server && docker compose down.
  3. Change image version: Edit docker-compose.yml and update the AppFlowy image tag (e.g., from 0.4.0 to 0.4.1).
  4. Start services: docker compose up -d. Docker will download the new image and recreate the AppFlowy container.
  5. Check logs: Ensure that everything started correctly.
A similar process applies to updating the PostgreSQL image, but with even greater caution and an mandatory backup, as database updates can be more complex.

Conclusions and Next Steps

Diagram: Conclusions and Next Steps
Diagram: Conclusions and Next Steps

Congratulations! You have successfully deployed AppFlowy Server on your VPS, creating a powerful and secure platform for knowledge and project management that is entirely under your control. You have mastered key aspects of working with Docker Compose, PostgreSQL, and automatic HTTPS via Caddy, gaining valuable experience in self-hosting and server administration.

Now that your system is operational, here are a few next steps for further development and optimization:

  • Monitoring and Logging: Set up a monitoring system (e.g., Prometheus + Grafana) to track VPS and container performance. Centralized logging (e.g., ELK Stack or Loki) will help identify and resolve issues more quickly.
  • Storage Scaling: If you plan to store very large volumes of files, consider mounting network storage (NFS, Ceph) or using object storage for files to avoid overloading the main VPS disk.
  • Integrations and Customization: Explore AppFlowy's capabilities for integration with other services or for extending functionality through plugins, should they become available.

Was this guide helpful?

Your feedback helps us improve our guides.

Share this post:

Send this guide to someone who may find it useful.

Telegram VKVK WhatsApp Facebook LinkedIn XX

AppFlowy VPS deployment: self-hosted Notion alternative with Docker and PostgreSQL
support_agent
Valebyte Support
Usually replies within minutes
Hi there!
Send us a message and we'll reply as soon as possible.