Install on a VPS (Control Panel)

Use this guide for a server you manage with a control panel such as CloudPanel, cPanel/WHM, Plesk, RunCloud or HestiaCP. Threadline's files live on the server's disk and PHP runs directly, with no Docker for the app itself. It is the shared-hosting install plus three things a VPS lets you add: Redis, Qdrant (the vector database) and a background queue worker.

Using aaPanel? Follow Install: VPS (aaPanel) instead. It walks through every aaPanel screen, including two settings that are easy to miss. No panel at all? Follow Install: VPS (Ubuntu, no panel), which shows every command.

This is the most hands-on way to install. If you are comfortable with Docker, Install: VPS (Docker) starts everything with one command. If you only have shared hosting, use Install: Shared Hosting.

Do you need this guide?

A VPS with a control panel can also run Threadline exactly like shared hosting: follow Install: Shared Hosting, leave SERVER=shared as it is, and add the one cron job. There is no Redis, no Qdrant and no worker to manage, and the built-in updater works the same way. Choose this guide instead when you expect heavy traffic or many customers and want Redis and Qdrant. You tell Threadline which one with a single line in .env:

Line in .env What runs
SERVER=shared (the default) MySQL only. Cron runs the queue. No extra services.
SERVER=vps Redis, Qdrant and a queue worker you keep running. This guide.

Docker and Dokploy installs set this for you, so there is nothing to type there. How the files are stored (disk or container) is detected automatically.

What you need

Check PHP's disabled functions. Some panels switch off PHP functions for safety, and Threadline fails quietly without these: proc_open (every scheduled task needs it), pcntl_signal and pcntl_alarm (the queue worker stops at once without them) and symlink (uploaded logos need it). They must not appear in the disable_functions list of the PHP version your site uses. The installer's first page checks for them.

Steps

  1. Create the site and the database. In your panel, add the domain, turn on HTTPS, choose PHP 8.3 or newer for it, and create an empty database with its own user. Note the database name, user and password.
  2. Point the web root at public. Upload the Threadline ZIP you downloaded and unzip it into the site's folder, then set the site's document root (some panels call it run directory) to the public folder inside it. The rest of the folder must not be reachable from the web. Upload the ZIP into the site user's own folder, not into /root, so that user can unzip it. Most panels have a "Laravel" template that sets up nginx for you. If you wrote the nginx site yourself, it needs both the Laravel rule and a PHP block (replace the socket path with your PHP-FPM one):
    root /var/www/threadline/public;
    index index.php;
    client_max_body_size 64M;
    
    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }
    
    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/php8.3-fpm.sock;
    }
    
    location ~ /\.(?!well-known).* {
        deny all;
    }
  3. Install Redis. On Ubuntu or Debian:
    sudo apt-get install -y redis-server
    redis-cli ping
    The second command should answer PONG. Redis listens on the local machine only by default. Leave it that way.
  4. Install Qdrant. If Docker is available (curl -fsSL https://get.docker.com | sh), run this once. It keeps Qdrant private to the server and starts it again after a reboot:
    docker run -d --name qdrant --restart unless-stopped \
      -p 127.0.0.1:6333:6333 \
      -v qdrant_data:/qdrant/storage \
      qdrant/qdrant:v1.20.0
    Check that it answers:
    curl http://127.0.0.1:6333/readyz
    Qdrant also publishes ready-made Linux downloads if you would rather not use Docker. Whichever you use, Threadline only needs it reachable at one address. Do not open port 6333 to the internet.
  5. Fill in your settings file. The package comes with a .env file in the site's folder. Open it and set these lines (change the ones that already exist; add the rest):
    SERVER=vps
    QUEUE_CONNECTION=redis
    CACHE_STORE=redis
    SESSION_DRIVER=redis
    REDIS_CLIENT=predis
    REDIS_HOST=127.0.0.1
    REDIS_PORT=6379
    QDRANT_URL=http://127.0.0.1:6333

    All three of QUEUE_CONNECTION, CACHE_STORE and SESSION_DRIVER must say redis. The example file sets them to database, and a value in .env wins over what SERVER=vps would choose.

    If you gave Redis a password, also set REDIS_PASSWORD. Do not fill in the database lines by hand; the installer asks for them.
  6. Give PHP the right to write. The user your site's PHP runs as must own the files, or at least be able to write to them. This also lets the built-in updater work later. The user is shown in your panel (often www, www-data or the site's own name):
    sudo chown -R SITE_USER:SITE_USER /path/to/threadline
    sudo chmod -R ug+rwX storage bootstrap/cache
  7. Run the installer. Open your domain in a browser. Threadline shows the same installer as on shared hosting: a requirements check, your site URL, the database details from step 1, and your Owner account. Follow the steps in the shared-hosting guide from there. It builds the database and loads the default languages, navigation, footer and blank settings. It does not create any sample accounts or plans: the only account is the Owner you create.

    Use an empty database. The installer drops any tables already in it.

    When it has finished, make .env readable by the site's user only. It holds the database password and the app key:
    chmod 600 /path/to/threadline/.env
    ls -l /path/to/threadline/.env
    The second command should start with -rw------- and name SITE_USER as the owner. If another user owns it, run the chown from the previous step first, or the site stops loading.
  8. Start the queue worker. On a VPS Threadline does not run jobs from cron, so a worker must always be running. Without it no message is sent. Add a supervisor program (in your panel's supervisor screen, or a file in /etc/supervisor/conf.d/threadline-worker.conf):
    [program:threadline-worker]
    command=/path/to/php artisan queue:work redis --queue=default,mail,webhooks --sleep=3 --tries=3 --max-time=3600
    directory=/path/to/threadline
    user=SITE_USER
    autostart=true
    autorestart=true
    stopasgroup=true
    redirect_stderr=true
    stdout_logfile=/path/to/threadline/storage/logs/worker.log
    stopwaitsecs=3600
    Then load it:
    sudo supervisorctl reread
    sudo supervisorctl update
    sudo supervisorctl status
    The status should say RUNNING. /path/to/php is the full path of the PHP your site uses (run which php, or look for it in your panel). A server can have several PHP versions, and a bare php may be another one. The worker must run as the same user as the website, never as root.
  9. Add the cron job. The scheduler handles reminders, campaigns and clean-up. Add this to the cron of the same user (your panel's cron screen, or crontab -e -u SITE_USER):
    * * * * * cd /path/to/threadline && /path/to/php artisan schedule:run >> /dev/null 2>&1
    Use the same full PHP path as for the worker. If your panel's cron screen offers a user, choose the website's user, not root. See Cron Setup.

Optional: send public pages as finished HTML

Search engines read your public pages (home, pricing, about and the others) best when the server sends them already built. With SERVER=vps Threadline does that as soon as a small Node.js program runs next to it. Without it, pages are built in the visitor's browser as before, and nothing else changes. Screens behind the sign-in are always built in the browser.

  1. Install Node.js 18 or newer, from your panel's Node.js screen or with sudo apt-get install -y nodejs. Find its full path with which node.
  2. Add a second supervisor program, the same way as the worker:
    [program:threadline-ssr]
    command=/path/to/node /path/to/threadline/bootstrap/ssr/app.js
    directory=/path/to/threadline
    user=SITE_USER
    autostart=true
    autorestart=true
    redirect_stderr=true
    stdout_logfile=/path/to/threadline/storage/logs/ssr.log
    Load it with the same three supervisorctl commands. It must say RUNNING.
  3. Open your home page in a private window and view the page source: the page's text is in it, not only an empty <div id="app">.

It uses about 100 MB of memory. An update restarts it on the new version by itself.

Starting on shared mode and moving later

You can install in shared mode first and move to SERVER=vps when you outgrow it. Install Redis and Qdrant (steps 3 and 4), then open Maintenance → Server, check readiness, run the reindex and press Switch. That button only changes the SERVER line. Afterwards, also set QUEUE_CONNECTION, CACHE_STORE and SESSION_DRIVER to redis (step 6) and start the queue worker (step 9). Until the queue is on Redis, Threadline keeps running its cron-driven worker, so nothing is left unprocessed in between.

After you install

Updating

Because the files are on disk, you can update from the browser (Maintenance → App Update, upload the new ZIP) or over SSH with php artisan threadline:update. Both back up the database, replace the files, apply database changes and tell the queue worker to restart; supervisor brings it back on its own. Full steps are in Updating Threadline.

If something goes wrong

What you see Likely cause and fix
The installer shows, but styling is missing or you get a 404 on every page except the home page The document root is not the public folder, or the nginx try_files rule is missing (step 2).
A "permission denied" or "unable to write" error The PHP user cannot write to storage or bootstrap/cache (step 7).
The cron job runs, but scheduled tasks never start proc_open is in PHP's disable_functions list. Remove it (see the warning above the steps).
An error mentioning open_basedir restriction in effect in storage/logs Your panel limits PHP to the site's folder. Threadline works with that restriction. If the message names a folder Threadline needs, add it to the site's allowed paths in your panel, or ask us for help with the exact message.
An uploaded logo does not show The storage link is missing. Run php artisan storage:link as the website's user in the Threadline folder. It needs symlink to be enabled in PHP.
Pages load but nothing is ever sent The queue worker is not running (step 9), or QUEUE_CONNECTION is not redis (step 6).
"Connection refused" mentioning Redis or port 6379 Redis is not running. Check redis-cli ping (step 3).
Knowledge-base answers are empty after you add documents Qdrant is not reachable at QDRANT_URL. Check curl http://127.0.0.1:6333/readyz (step 4).

More in Troubleshooting & FAQ.