RAG Engine
    Document AI

    Deploy RAGFlow on a VPS

    Self-host RAGFlow, an open-source retrieval-augmented generation engine with document parsing and a web UI, on a RamNode VPS with Docker Compose and nginx TLS.

    RAGFlow is a RAG (retrieval-augmented generation) engine with document parsing, chunking, and a web UI. It ships as a multi-container Docker stack (server, MySQL/Postgres, Elasticsearch or Infinity, Redis, MinIO), so it's memory-hungry — plan for a real VPS, not the smallest tier.

    1. Prerequisites

    • A RamNode VPS running Ubuntu 24.04 LTS (KVM plan recommended)
    • Minimum 16 GB RAM, 4 vCPUs, 50 GB disk — RAGFlow's default stack (Elasticsearch + MySQL + MinIO + Redis + the app server) is heavy; 8 GB will swap constantly under real document loads
    • A domain or subdomain pointed at the VPS's public IP (A record) — e.g. ragflow.yourdomain.com
    • Root or sudo SSH access

    2. Initial server setup

    shell
    apt update && apt -y upgrade
    apt -y install curl git ufw
    
    # Basic firewall
    ufw allow OpenSSH
    ufw allow 80/tcp
    ufw allow 443/tcp
    ufw enable

    Increase vm.max_map_count, which Elasticsearch requires:

    shell
    echo "vm.max_map_count=262144" >> /etc/sysctl.conf
    sysctl -p

    3. Install Docker Engine + Compose plugin

    shell
    curl -fsSL https://get.docker.com | sh
    apt -y install docker-compose-plugin
    systemctl enable --now docker

    4. Pull RAGFlow and configure

    shell
    git clone https://github.com/infiniflow/ragflow.git /opt/ragflow
    cd /opt/ragflow/docker

    Edit .env in this directory — the key settings to review:

    • RAGFLOW_IMAGE — use the :v0.x.x-slim tag unless you need embedded models baked into the image (the slim image is far smaller and faster to pull)
    • MYSQL_PASSWORD, MINIO_PASSWORD, ELASTIC_PASSWORD — set strong unique passwords, not the defaults
    • TIMEZONE — set to your local zone
    • DOC_ENGINEelasticsearch (default) or infinity (lighter weight, fewer moving parts if RAM is tight)

    If you're memory-constrained, switch DOC_ENGINE=infinity and use the corresponding docker-compose.yml variant included in the repo (docker-compose-base.yml swaps the engine).

    5. Bring up the stack

    shell
    docker compose -f docker-compose.yml up -d

    First boot pulls several large images and can take a few minutes. Watch logs until the server reports it's listening:

    shell
    docker logs -f ragflow-server

    By default the web UI listens on port 80 inside the compose network, mapped to the host. Confirm with:

    shell
    docker compose ps

    6. Put nginx in front with TLS

    Don't expose RAGFlow's container port directly to the internet long-term — front it with nginx and Let's Encrypt so you get HTTPS and can layer on rate limiting later.

    shell
    apt -y install nginx certbot python3-certbot-nginx

    Create /etc/nginx/sites-available/ragflow:

    shell
    server {
        listen 80;
        server_name ragflow.yourdomain.com;
    
        client_max_body_size 100M;   # document uploads can be large
    
        location / {
            proxy_pass http://127.0.0.1:9380;   # RAGFlow's host-mapped port; check docker compose ps
            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 600s;   # large document parsing can be slow
        }
    }
    shell
    ln -s /etc/nginx/sites-available/ragflow /etc/nginx/sites-enabled/
    nginx -t && systemctl reload nginx
    certbot --nginx -d ragflow.yourdomain.com

    Since nginx now owns 80/443, you can remove RAGFlow's own port 80 mapping in docker-compose.yml if it conflicts, or just bind it to 127.0.0.1 only.

    7. First login and model setup

    Visit https://ragflow.yourdomain.com, register the first admin account, then under Settings → Model Providers add your LLM/embedding provider (OpenAI-compatible endpoint, Ollama, etc.). RAGFlow doesn't ship a model itself — it orchestrates calls to whatever provider you configure.

    8. Backups

    The stateful pieces live in Docker volumes: MySQL/Postgres data, MinIO (uploaded documents), and the Elasticsearch/Infinity index. At minimum, cron a nightly dump:

    shell
    docker exec ragflow-mysql mysqldump -u root -p"$MYSQL_PASSWORD" --all-databases > /root/backups/ragflow-mysql-$(date +%F).sql
    docker run --rm -v ragflow_minio_data:/data -v /root/backups:/backup alpine \
      tar czf /backup/ragflow-minio-$(date +%F).tar.gz -C /data .

    Rotate old backups off-box (RamNode object storage or another VPS) rather than keeping them on the same disk.

    9. Updating

    shell
    cd /opt/ragflow/docker
    docker compose down
    git pull
    docker compose pull
    docker compose up -d

    Check the release notes first — RAGFlow has had breaking config/schema changes between minor versions.

    Troubleshooting notes

    • Elasticsearch container exits immediately → almost always the vm.max_map_count setting from step 2 wasn't applied, or RAM is too low for ES's default heap.
    • Uploads fail past a certain size → check both nginx's client_max_body_size and RAGFlow's own upload limit env var.
    • Slow document parsing → parsing is CPU-bound; on a 4 vCPU box, expect large PDFs to take real time. Scale vCPUs before assuming something's broken.