Deploying a Laravel App to a VPS with Neo

A complete .neo.yml for Laravel: encrypted secrets, migrations on deploy, and a queue worker — no PaaS, no control plane.

7 min read

The shape

A typical Laravel deploy needs four things beyond a bare Dockerfile: secrets that don't live in plaintext in git, migrations run against the new code before it takes traffic, a queue worker, and storage:link on first boot.

Secrets: encrypted, not gitignored-and-hoped-for

php artisan env:encrypt

Commit the resulting .env.encrypted; keep .env out of git.

env_encrypted: .env.encrypted

Neo decrypts it in memory at deploy — the key never touches disk on the server. If you don't have PHP handy on a given machine, neo env encrypt / neo env decrypt read and write the exact same format.

Migrations and setup, run in the right place

release:
  - php artisan migrate --force
  - php artisan storage:link
  - php artisan config:cache

These run inside the new container, after its health check, before traffic switches — see Running Database Migrations Safely.

The queue worker

workers:
  queue:
    command: php artisan queue:work --tries=3 --max-time=3600

Shares the app's image and environment, runs as its own container (app-shop-worker-queue), and restarts automatically if it exits.

Putting it together

name: shop
port: 8080
env_encrypted: .env.encrypted

release:
  - php artisan migrate --force
  - php artisan storage:link
  - php artisan config:cache

workers:
  queue:
    command: php artisan queue:work --tries=3

environments:
  production:
    server: prod-box
    domain: shop.example.com
neo deploy --to production

If migrate --force fails against real data, the new container is torn down and the old one keeps serving — you get a failed deploy notification, not a broken production app.

Try it yourself — Neo is free, with every feature unlocked.

Get started free