I ran VitoDeploy's install.sh on Ubuntu — it failed because the VPS already had Nginx

I ran VitoDeploy's install.sh on Ubuntu — it failed because the VPS already had Nginx

I wanted a Forge-shaped panel on a box I own. VitoDeploy is Laravel. On Ubuntu 24.04 the official install.sh died because the VPS already had a LEMP stack — the script expects a fresh machine.

Vito Deploy

Introduction

I wanted to provision servers, ship Laravel sites, manage databases, SSL, cron, and Supervisor workers from a browser — without paying Forge. VitoDeploy (Vito) is a self-hosted panel: Laravel, Inertia, React, Tailwind. It SSHes into other machines (and can manage itself if you are careful). I do not install app sites on the same VPS as Vito if I can avoid it. Their docs say that for a reason.

I used the one-command Ubuntu 24.04 installer. Docker exists for a look. On a fresh Ubuntu 24.04 box this install is famous for failing when Nginx/MySQL/PHP are already there, and for a 401/login loop when APP_URL does not match the URL you actually open.

Where it broke

1. install.sh fails on a used VPS

On a fresh Ubuntu 24.04 box this is the failure this install is famous for — except the box was not fresh. Leftover Nginx, MySQL, or PHP makes the script abort or leave a half panel.

Shape of the problem: curl of scripts/install.sh exits mid-way, or you get port 80 already bound.

# typical: nginx/apache already listening, or apt packages the script wanted to own

Fix from this post: fresh Ubuntu 24.04, no prior LEMP. Check ports before you run the installer. I would not invent extra installer flags; the script is what the project ships.

ss -tlnp | grep -E ':80|:443'

If 80/443 are taken, use a new droplet or uninstall the old stack yourself. I am not documenting a reverse-engineered “force install.”

2. Login 401 / odd URLs — APP_URL wrong

After install, opening the IP while .env has a hostname (or the reverse) breaks sessions and signed URLs.

This post already says: set APP_URL correctly and restart workers.

cd /home/vito/vito
# edit APP_URL in .env to the exact URL you use in the browser
php artisan optimize:clear
php artisan optimize
sudo supervisorctl restart worker:*

Storage permission 500s are the usual Laravel leftover if you poked files as root:

chown -R www-data:www-data storage/
php artisan storage:link

(User may be vito on their layout — match whatever the installer created.)

Prerequisites

VPS install:

  • Fresh Ubuntu 24.04 LTS (others untested in their script)
  • 1 CPU, 1 GB RAM, 20+ GB disk minimum
  • Ports 80, 443, SSH
  • Root SSH
  • Domain optional but better for TLS

Docker install: Docker + Compose on Linux/macOS/Windows+WSL.

Also: GitHub account for source control later, cloud API keys if you provision droplets from the UI, SMTP for notifications.

Dedicated server for Vito. Apps go on managed servers, not alongside the panel if you can help it.

Installation Guide

Two paths: official VPS script, or Docker.

Method 1: Fresh Ubuntu VPS (what I would run for real)

  1. Provision Ubuntu 24.04.
  2. SSH as root:
ssh root@your-server-ip
  1. Update:
apt update && apt upgrade -y
apt install curl -y
  1. Official installer:
bash <(curl -Ls https://raw.githubusercontent.com/vitodeploy/vito/3.x/scripts/install.sh)

Prompts: admin email, admin password. Or:

ADMIN_EMAIL=admin@yourdomain.com ADMIN_PASSWORD=StrongPass123 bash <(curl -Ls https://raw.githubusercontent.com/vitodeploy/vito/3.x/scripts/install.sh)

Wait 5–10 minutes. You should see output like:

🎉 Congratulations!
✅ SSH User: SSH-USER
✅ SSH Password: SSH-PASSWORD
✅ Admin Email: ADMIN-EMAIL
✅ Admin Password: ADMIN-PASSWORD
  1. Open http://your-server-ip and log in.

Save the SSH user the script printed. That is how Vito lives on the box.

Method 2: Docker

docker run -v vito_storage:/var/www/html/storage \
    -v vito_plugins:/var/www/html/app/Vito/Plugins \
    -e APP_KEY=base64:Your32CharacterRandomAppKeyHere1234567890== \
    -e NAME=admin \
    -e EMAIL=admin@example.com \
    -e PASSWORD=StrongPass123 \
    -e APP_URL=http://your-domain-or-ip \
    -p 80:80 vitodeploy/vito:latest

Compose:

version: '3'
services:
  vito:
    image: vitodeploy/vito:latest
    ports:
      - '8000:80'
    environment:
      APP_KEY: 'base64:Your32CharacterRandomAppKeyHere1234567890=='
      NAME: 'admin'
      EMAIL: 'admin@example.com'
      PASSWORD: 'StrongPass123'
      APP_URL: 'http://localhost:8000'
    volumes:
      - 'vito-storage:/var/www/html/storage'
      - 'vito-plugins:/var/www/html/app/Vito/Plugins'
volumes:
  vito-storage:
    driver: local
  vito-plugins:
    driver: local

Generate APP_KEY with openssl rand -hex 32 or php artisan key:generate --show. ARM: vitodeploy/vito:3.x-arm64.

Named volumes or you lose the panel on recreate.

Configuration

VPS .env is typically /home/vito/vito/.env. Docker: environment variables.

APP_NAME=VitoDeploy
APP_ENV=production
APP_KEY=base64:YourRandomKeyHere==
APP_DEBUG=false
APP_URL=https://vito.yourdomain.com

DB_CONNECTION=sqlite

MAIL_MAILER=smtp
MAIL_HOST=smtp.example.com
MAIL_PORT=587
MAIL_USERNAME=your-smtp-user
MAIL_PASSWORD=your-smtp-pass
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=noreply@yourdomain.com

FORCE_HTTPS=true

SQLite is the default in this post. MySQL if you switched.

cd /home/vito/vito
php artisan optimize:clear
php artisan optimize
sudo supervisorctl restart worker:*

Domain/SSL from the dashboard (Server Settings → add domain, Let’s Encrypt). SSH keys: Vito generates a pair for managing other servers.

Packages information

VitoDeploy is Laravel. install.sh hides Composer. You still operate a Laravel app:

  • Laravel.env, APP_KEY, APP_URL, storage/, artisan optimize, storage:link. The 401 after a hostname change is this layer.
  • Queues / Supervisor — this post already restarts worker:*. Deploys, SSL, and remote SSH jobs sit on workers. If Supervisor is down, the UI clicks and nothing happens on the target server.
  • Inertia + React — frontend of the panel. You do not npm-build on the VPS install path unless you are developing Vito itself.

I am not listing Spatie or Horizon unless you confirmed them in their composer.json. The worker is Laravel’s queue under Supervisor.

Usage

  1. Open the dashboard, log in.
  2. Add a server: IP, SSH user/key. Vito provisions Nginx, PHP, MySQL, Redis, Supervisor, UFW on that machine.
  3. Sites: create a site, GitHub repo, PHP version, deploy hooks (composer install, php artisan migrate). Deploy.

Also in the UI: databases, firewall rules, cron, queue workers on the managed server, browser console, CPU/memory graphs.

Test with a tiny Laravel app or WordPress, then read the deploy log. Architecture: Vito (Laravel) SSHes out and installs a LEMP stack. The panel host stays the panel.

Troubleshooting

  • Installer fails: fresh Ubuntu 24.04, no prior LEMP, ports 80/443 free.
  • APP_URL / 401: exact URL in .env, then optimize + restart worker:*.
  • SSL: 80/443 open; Certbot from the UI or by hand if the UI stalls.
  • Permissions: chown on storage/, php artisan storage:link.
  • Database: SQLite file permissions or MySQL creds.
  • Slow: more RAM; php artisan optimize.
  • Docker data gone: named volumes as above.

Logs: dashboard or storage/logs. Discord if you are stuck on a script error I did not hit.

A feature I would not have found from the homepage

The panel has its own queue. Clicking “deploy” or “issue certificate” enqueues work. If supervisorctl restart worker:* never happened after an .env edit, the UI looks fine and the remote server does nothing. That is the same class of bug as Invoice Ninja’s idle Redis — except here the worker lives on the Vito host.

Do not host customer sites on the Vito VPS. The installer builds a management node. Putting a public Laravel app on the same Nginx that serves the panel is how you fight the next install.sh or a PHP version the panel expected to own. Add a second cheap VPS as the first “server” in the UI and deploy there.

APP_URL must be the URL in the address bar. IP today, hostname tomorrow, without updating .env, is how you get 401s and broken Let’s Encrypt callbacks. FORCE_HTTPS=true only after TLS actually terminates on that name.

Backups: /home/vito/vito/storage (and the SQLite file if you stayed on SQLite). Losing that directory is losing the panel’s memory of every server and site, not just logs.

Day two on the VPS

Vito is a control plane. I would lock it down before I add a production app server:

  • Freshness: if I ever re-run install.sh, it is on a new droplet. I would not “fix” a half-installed LEMP host by curling the script again.
  • Workers: sudo supervisorctl status after every .env change. Deploy buttons that do nothing are workers, not GitHub.
  • SSH keys: Vito’s key on the managed server is how every future deploy works. I would back up that key material with storage/, not screenshot it.
  • Firewall on the panel: 80/443/SSH. The managed servers can be tighter (SSH from the panel IP only) once I trust the first deploy.
  • SMTP: password-reset and job failure mail. If mail is unset, I watch the dashboard logs instead of assuming a deploy succeeded.
  • Updates: follow their docs for the 3.x path. I would snapshot the VPS or copy storage/ first. This is still Laravel — artisan migrate may run as part of their upgrade. I am not inventing extra installer flags.

I would add GitHub, then one throwaway Laravel site on the second VPS, read the deploy log, then consider a real app.

Conclusion

VitoDeploy is running on a fresh Ubuntu 24.04 VPS from their install.sh: panel up, Supervisor workers, SQLite (or whatever the script set). Next I would put a real hostname on APP_URL, force HTTPS, back up /home/vito/vito/storage, and add a second VPS as the first managed server so I am not deploying sites onto the panel box. Then GitHub integration and one Laravel deploy I can watch in the log.

References

Did you hit the same wall?

I got stuck on install.sh dying because Nginx was already bound on port 80 — the script wants a fresh Ubuntu 24.04 VPS. Did you hit the same thing, or a different one — Composer memory, storage permissions, PHP extensions, a queue worker that never started? Tell me in the comments. I read them.

Need this done on your server?

I deploy and harden Laravel/CodeCanyon apps on cPanel or VPS, and offer monthly Server Watch retainers. Hire for deploy · Care plan

Share:

Get new posts in your inbox

No spam. One short email per new article — practical PHP, Laravel, devops, and AI-assisted workflows.

Comments

Powered by GitHub Discussions via Giscus. A free GitHub account is required.