Document Management
    PostgreSQL + Solr

    Deploy Docspell on a VPS

    Self-host Docspell, a document management and archiving system, on a RamNode VPS with Docker Compose, PostgreSQL, and Solr full-text search.

    Docspell is a self-hosted document management/archiving system. This guide deploys it with Docker Compose, using PostgreSQL as the database and Solr for full-text search — the standard production setup.

    Target environment: Ubuntu 24.04 LTS, 2+ vCPU, 4 GB+ RAM (Solr and the OCR pipeline are the main memory consumers), 40 GB+ disk depending on document volume.


    1. Initial server prep

    SSH into your RamNode VPS as root, then:

    shell
    apt update && apt upgrade -y
    apt install -y curl ufw fail2ban
    
    # Create a non-root deploy user
    adduser deploy
    usermod -aG sudo deploy
    
    # Basic firewall
    ufw allow OpenSSH
    ufw allow 80/tcp
    ufw allow 443/tcp
    ufw enable

    Log out and back in as deploy for the rest of this guide.

    2. Install Docker and Docker Compose

    shell
    curl -fsSL https://get.docker.com | sudo sh
    sudo usermod -aG docker $USER
    newgrp docker
    
    # Verify
    docker --version
    docker compose version

    3. Create the project layout

    shell
    mkdir -p ~/docspell/{data/postgres,data/solr,data/docspell,data/imap}
    cd ~/docspell

    4. Create the Compose file

    shell
    nano docker-compose.yml
    shell
    services:
      db:
        image: postgres:16
        restart: unless-stopped
        environment:
          POSTGRES_DB: docspell
          POSTGRES_USER: docspell
          POSTGRES_PASSWORD: "CHANGE_ME_DB_PASSWORD"
        volumes:
          - ./data/postgres:/var/lib/postgresql/data
        networks: [docspell]
    
      solr:
        image: solr:9
        restart: unless-stopped
        command: solr-precreate docspell
        volumes:
          - ./data/solr:/var/solr
        networks: [docspell]
    
      restserver:
        image: docspell/restserver:latest
        restart: unless-stopped
        depends_on: [db, solr]
        ports:
          - "127.0.0.1:7880:7880"
        environment:
          DOCSPELL_SERVER_INTERNALURL: "http://restserver:7880"
          DOCSPELL_SERVER_BASEURL: "https://docspell.yourdomain.com"
          DOCSPELL_SERVER_BACKEND_JDBC_URL: "jdbc:postgresql://db:5432/docspell"
          DOCSPELL_SERVER_BACKEND_JDBC_USER: "docspell"
          DOCSPELL_SERVER_BACKEND_JDBC_PASSWORD: "CHANGE_ME_DB_PASSWORD"
          DOCSPELL_SERVER_FULLTEXTSEARCH_SOLR_URL: "http://solr:8983/solr/docspell"
        volumes:
          - ./data/docspell:/opt/docspell
        networks: [docspell]
    
      joex:
        image: docspell/joex:latest
        restart: unless-stopped
        depends_on: [db, solr]
        environment:
          DOCSPELL_JOEX_JDBC_URL: "jdbc:postgresql://db:5432/docspell"
          DOCSPELL_JOEX_JDBC_USER: "docspell"
          DOCSPELL_JOEX_JDBC_PASSWORD: "CHANGE_ME_DB_PASSWORD"
          DOCSPELL_JOEX_FULLTEXTSEARCH_SOLR_URL: "http://solr:8983/solr/docspell"
          DOCSPELL_JOEX_BASEURL: "http://joex:7878"
        volumes:
          - ./data/docspell:/opt/docspell
        networks: [docspell]
    
    networks:
      docspell:
        driver: bridge

    Replace CHANGE_ME_DB_PASSWORD (both occurrences must match) and docspell.yourdomain.com with your actual values.

    Joex is the job executor that runs OCR and text extraction — it needs to share the docspell data volume with restserver and needs enough CPU/RAM to run Tesseract comfortably.

    5. Bring the stack up

    shell
    docker compose up -d
    docker compose logs -f restserver

    Wait until you see the server report it's listening, then Ctrl+C out of the log follow.

    6. Put Nginx + TLS in front of it

    shell
    sudo apt install -y nginx certbot python3-certbot-nginx
    shell
    sudo nano /etc/nginx/sites-available/docspell
    shell
    server {
        listen 80;
        server_name docspell.yourdomain.com;
    
        client_max_body_size 200M;
    
        location / {
            proxy_pass http://127.0.0.1:7880;
            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;
        }
    }
    shell
    sudo ln -s /etc/nginx/sites-available/docspell /etc/nginx/sites-enabled/
    sudo nginx -t && sudo systemctl reload nginx
    sudo certbot --nginx -d docspell.yourdomain.com

    Point your domain's A record at the VPS's IP before running certbot.

    7. First login and setup

    1. Visit https://docspell.yourdomain.com.
    2. Register a new collective (Docspell's term for a tenant/organization).
    3. Create your admin user under that collective.
    4. Log in and confirm document upload + OCR works by uploading a test PDF and checking it becomes searchable after a minute.

    8. Backups

    At minimum, back up:

    • ~/docspell/data/postgres (metadata)
    • ~/docspell/data/docspell (uploaded file blobs)

    Solr's index (~/docspell/data/solr) can be rebuilt from the documents if lost, so it's lower priority.

    shell
    # Example: nightly tarball to a separate location
    tar -czf docspell-backup-$(date +%F).tar.gz -C ~/docspell data/postgres data/docspell

    9. Updating

    shell
    cd ~/docspell
    docker compose pull
    docker compose up -d

    Check the Docspell release notes before upgrading across major versions — schema migrations run automatically on restserver startup but it's worth reading first.