Install on a VPS

On this page

This guide installs Inventoros from source on a VPS with full root access. Examples cover Ubuntu 22.04+, CentOS / RHEL, and Debian, then the scheduler and queue worker, SSL and firewall setup. Inventoros needs PHP 8.4.1 or newer (8.4 and 8.5) and Node.js 22.12 or newer to build the frontend.

Ubuntu 22.04+

  1. Update system packages:
sudo apt update && sudo apt upgrade -y
  1. Install the required packages. Ubuntu's own repositories do not ship PHP 8.4, so add the ondrej/php PPA first:
sudo apt install -y software-properties-common
sudo add-apt-repository -y ppa:ondrej/php
sudo apt update
sudo apt install -y php8.4 php8.4-cli php8.4-fpm php8.4-mysql \
    php8.4-mbstring php8.4-xml php8.4-bcmath php8.4-curl \
    php8.4-zip php8.4-gd php8.4-intl nginx mysql-server \
    git curl unzip
  1. Install Composer:
curl -sS https://getcomposer.org/installer | php
sudo mv composer.phar /usr/local/bin/composer
sudo chmod +x /usr/local/bin/composer
  1. Install Node.js and npm:
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
sudo apt install -y nodejs
  1. Set up the MySQL database:
sudo mysql -e "CREATE DATABASE inventoros;"
sudo mysql -e "CREATE USER 'inventoros'@'localhost' IDENTIFIED BY 'secure_password';"
sudo mysql -e "GRANT ALL PRIVILEGES ON inventoros.* TO 'inventoros'@'localhost';"
sudo mysql -e "FLUSH PRIVILEGES;"
  1. Clone and set up Inventoros:
cd /var/www
sudo git clone https://github.com/Inventoros/Inventoros.git inventoros
cd inventoros
sudo composer install --no-dev --optimize-autoloader
sudo npm install && sudo npm run build
sudo cp .env.example .env
sudo php artisan key:generate
  1. Configure the environment. Edit .env and update these values:
sudo nano .env

# Update these values:
APP_ENV=production
APP_DEBUG=false
APP_URL=http://your-domain.com
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=inventoros
DB_USERNAME=inventoros
DB_PASSWORD=secure_password

.env.example ships with DB_CONNECTION=sqlite and the other DB_ lines commented out, so set all six (remove the leading #). If you finish setup with the web installer (step 10), its database step writes these values for you.

  1. Set permissions:
sudo chown -R www-data:www-data /var/www/inventoros
sudo chmod -R 755 /var/www/inventoros
sudo chmod -R 775 /var/www/inventoros/storage
sudo chmod -R 775 /var/www/inventoros/bootstrap/cache
  1. Configure Nginx. Create the site configuration:
# /etc/nginx/sites-available/inventoros
server {
    listen 80;
    server_name your-domain.com;
    root /var/www/inventoros/public;

    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-Content-Type-Options "nosniff";

    index index.php;
    charset utf-8;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location = /favicon.ico { access_log off; log_not_found off; }
    location = /robots.txt  { access_log off; log_not_found off; }

    error_page 404 /index.php;

    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php/php8.4-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include fastcgi_params;
    }

    location ~ /\.(?!well-known).* {
        deny all;
    }
}

Enable the site:

sudo ln -s /etc/nginx/sites-available/inventoros /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl restart nginx
  1. Access the web installer. Visit your domain to complete setup:
http://your-domain.com/install

The database step writes the connection you enter to .env (DB_CONNECTION, host, database and credentials) and creates the tables in that database. Finishing the installer (creating the admin account) sets APP_ENV=production and APP_DEBUG=false, so a site set up with the example .env does not stay in debug mode.

CentOS / RHEL

  1. Update the system and enable EPEL and Remi:
sudo dnf update -y
sudo dnf install -y epel-release
sudo dnf install -y https://rpms.remirepo.net/enterprise/remi-release-$(rpm -E %rhel).rpm
  1. Install PHP 8.4:
sudo dnf module reset php
sudo dnf module enable php:remi-8.4
sudo dnf install -y php php-cli php-fpm php-mysqlnd php-mbstring \
    php-xml php-bcmath php-curl php-zip php-gd php-intl php-sodium

Remi packages Sodium separately as php-sodium; the in-app updater needs it to verify signed releases.

  1. Install MySQL and Nginx:
sudo dnf install -y mysql-server nginx git curl unzip
sudo systemctl enable --now mysqld nginx php-fpm
  1. Install Composer:
curl -sS https://getcomposer.org/installer | php
sudo mv composer.phar /usr/local/bin/composer
sudo chmod +x /usr/local/bin/composer
  1. Install Node.js and npm:
curl -fsSL https://rpm.nodesource.com/setup_22.x | sudo bash -
sudo dnf install -y nodejs
  1. Set up the MySQL database:
sudo mysql_secure_installation
sudo mysql -e "CREATE DATABASE inventoros;"
sudo mysql -e "CREATE USER 'inventoros'@'localhost' IDENTIFIED BY 'secure_password';"
sudo mysql -e "GRANT ALL PRIVILEGES ON inventoros.* TO 'inventoros'@'localhost';"
sudo mysql -e "FLUSH PRIVILEGES;"
  1. Clone and set up Inventoros, then edit .env as in Ubuntu step 7 (including APP_ENV=production and APP_DEBUG=false):
cd /var/www
sudo git clone https://github.com/Inventoros/Inventoros.git inventoros
cd inventoros
sudo composer install --no-dev --optimize-autoloader
sudo npm install && sudo npm run build
sudo cp .env.example .env
sudo php artisan key:generate
  1. Set permissions and SELinux contexts:
sudo chown -R nginx:nginx /var/www/inventoros
sudo chmod -R 755 /var/www/inventoros
sudo chmod -R 775 /var/www/inventoros/storage
sudo chmod -R 775 /var/www/inventoros/bootstrap/cache

# SELinux permissions (if enabled)
sudo setsebool -P httpd_can_network_connect 1
sudo chcon -R -t httpd_sys_rw_content_t /var/www/inventoros/storage
sudo chcon -R -t httpd_sys_rw_content_t /var/www/inventoros/bootstrap/cache
  1. Configure Nginx. Use the same server block as Ubuntu in /etc/nginx/conf.d/inventoros.conf, but change the PHP-FPM socket to fastcgi_pass unix:/run/php-fpm/www.sock;. PHP-FPM's default pool runs as apache, so set user = nginx and group = nginx in /etc/php-fpm.d/www.conf to match the file ownership from step 8. Then:
sudo nginx -t
sudo systemctl restart php-fpm nginx
  1. Configure the firewall:
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload

Debian

  1. Update system packages:
sudo apt update && sudo apt upgrade -y
  1. Add the PHP repository (for PHP 8.4):
sudo apt install -y lsb-release apt-transport-https ca-certificates curl
sudo curl -sSLo /usr/share/keyrings/deb.sury.org-php.gpg https://packages.sury.org/php/apt.gpg
echo "deb [signed-by=/usr/share/keyrings/deb.sury.org-php.gpg] https://packages.sury.org/php/ $(lsb_release -sc) main" | sudo tee /etc/apt/sources.list.d/php.list
sudo apt update
  1. Install the required packages:
sudo apt install -y php8.4 php8.4-cli php8.4-fpm php8.4-mysql \
    php8.4-mbstring php8.4-xml php8.4-bcmath php8.4-curl \
    php8.4-zip php8.4-gd php8.4-intl nginx mariadb-server \
    git curl unzip
  1. Install Composer:
curl -sS https://getcomposer.org/installer | php
sudo mv composer.phar /usr/local/bin/composer
sudo chmod +x /usr/local/bin/composer
  1. Install Node.js and npm:
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
sudo apt install -y nodejs
  1. Set up the MariaDB database:
sudo mysql_secure_installation
sudo mysql -e "CREATE DATABASE inventoros;"
sudo mysql -e "CREATE USER 'inventoros'@'localhost' IDENTIFIED BY 'secure_password';"
sudo mysql -e "GRANT ALL PRIVILEGES ON inventoros.* TO 'inventoros'@'localhost';"
sudo mysql -e "FLUSH PRIVILEGES;"
  1. The remaining steps are identical to Ubuntu. Clone the repo, install dependencies, build assets, configure .env, set permissions, and configure Nginx (Ubuntu steps 6 through 10):
cd /var/www
sudo git clone https://github.com/Inventoros/Inventoros.git inventoros
cd inventoros
sudo composer install --no-dev --optimize-autoloader
sudo npm install && sudo npm run build
sudo cp .env.example .env
sudo php artisan key:generate

Scheduler and queue worker

Every install needs the Laravel scheduler and a queue worker. The scheduler runs low-stock checks, scheduled cycle counts, scheduled report emails, shipment tracking, log pruning and removal of expired export files. The worker sends webhook deliveries and queued emails (purchase orders, invoices, approvals, shipment notices). Without them these jobs pile up and never run.

The repository ships systemd units in deploy/systemd/. Edit User, Group and WorkingDirectory in each file to match your server (nginx instead of www-data on CentOS / RHEL), then install them:

sudo cp /var/www/inventoros/deploy/systemd/inventoros-worker.service /etc/systemd/system/
sudo cp /var/www/inventoros/deploy/systemd/inventoros-scheduler.service /etc/systemd/system/
sudo cp /var/www/inventoros/deploy/systemd/inventoros-scheduler.timer /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now inventoros-worker
sudo systemctl enable --now inventoros-scheduler.timer

With the worker running, set QUEUE_RUN_VIA_SCHEDULER=false in .env. Otherwise the scheduler also starts a short-lived queue worker every minute (the fallback for shared hosts that have no worker).

If you prefer cron to the scheduler timer, add this to the web user's crontab (sudo crontab -u www-data -e) instead:

* * * * * cd /var/www/inventoros && php artisan schedule:run >> /dev/null 2>&1

After each deploy, run php artisan queue:restart so the worker picks up the new code.

Imports, exports and scheduled reports can run for up to 600 seconds. Queue reservations must last longer, or another worker can pick up a job that is still running. Database, Redis and Beanstalkd queues default to 660 seconds; keep any DB_QUEUE_RETRY_AFTER, REDIS_QUEUE_RETRY_AFTER or BEANSTALKD_QUEUE_RETRY_AFTER override above 600. For SQS, set the queue's visibility timeout above 600 seconds in AWS. After changing these settings, rebuild the configuration cache and restart workers.

Large exports are written to EXPORT_DISK and can be downloaded for EXPORT_RETENTION_DAYS (default 7) after they finish. After that the download link reports the export as expired, and the nightly exports:prune command deletes the file and its record, at most 1000 per run. Set EXPORT_RETENTION_DAYS=0 to keep exports forever.

SSL certificate

Ubuntu / Debian:

sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d your-domain.com
sudo systemctl restart nginx

CentOS / RHEL:

sudo dnf install -y certbot python3-certbot-nginx
sudo certbot --nginx -d your-domain.com
sudo systemctl restart nginx

Once the site answers over HTTPS, set APP_URL=https://your-domain.com and SESSION_SECURE_COOKIE=true in .env (the installer leaves it false when it ran over plain HTTP), then run php artisan config:cache if you cache the configuration.

Firewall

Ubuntu / Debian (UFW):

sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable

CentOS / RHEL (firewalld):

sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --permanent --add-service=ssh
sudo firewall-cmd --reload

Report an issue: https://github.com/Inventoros/Inventoros/issues