{
  "manifest_version": "1",
  "slug": "laravel",
  "title": "Laravel",
  "headline": "Deploy a Laravel app",
  "description": "Deploy a Laravel app to an exe.dev VM: git push to deploy, PHP 8.5 with nginx, SQLite on a persistent disk, queues, the scheduler, nightly backups.",
  "category": "frameworks",
  "tags": [
    "laravel",
    "php",
    "deploy laravel",
    "laravel hosting",
    "git push deploy",
    "sqlite",
    "nginx",
    "php-fpm",
    "queues"
  ],
  "url": "https://exe.dev/deploy/laravel",
  "markdown_url": "https://exe-staging.dev/deploy/laravel.md",
  "upstream": {
    "website": "https://laravel.com",
    "source": "https://github.com/laravel/laravel",
    "docs": "https://laravel.com/docs/13.x/deployment",
    "license": "MIT"
  },
  "example_vm_name": "my-laravel",
  "time": "15 minutes",
  "stack": [
    {
      "name": "Your Laravel app",
      "detail": "PHP 8.5 with PHP-FPM, as app.service; nginx serves it and public/",
      "port": 8000,
      "data": "~/app → the current release in ~/releases"
    },
    {
      "name": "git push deploys",
      "detail": "A pre-receive hook in ~/app.git builds, migrates, and switches",
      "data": "~/app.git"
    },
    {
      "name": "Queue worker and scheduler",
      "detail": "queue:work as app-worker.service; schedule:run every minute by app-schedule.timer"
    },
    {
      "name": "SQLite",
      "detail": "The database, uploads and the rest of storage/; settings in ~/app.env",
      "data": "~/data"
    },
    {
      "name": "Nightly backup",
      "detail": "app-backup.timer; database, uploads, settings and code; keeps 14",
      "data": "~/backups"
    }
  ],
  "access": [
    {
      "url": "https://my-laravel.exe.xyz/",
      "who": "private",
      "what": "Your app, until you make it public"
    },
    {
      "url": "ssh my-laravel.exe.xyz",
      "who": "private",
      "what": "git push to deploy; shell and logs"
    }
  ],
  "inputs": [
    {
      "name": "REPO",
      "description": "the GitHub repository to deploy, as owner/name or a URL, or `new` to start a fresh Laravel app"
    }
  ],
  "delivers": [
    "PHP 8.5, Composer and Node.js 24 installed; nginx answering on port 8000 and PHP-FPM running as app.service, both enabled to come back after a reboot.",
    "A ~/app.git whose pre-receive hook builds each push into a new release (Composer, the Vite build, migrations, optimize), switches to it, and checks /up; a failed deploy rejects the push and leaves the running release alone.",
    "Settings in ~/app.env (mode 600) with a generated APP_KEY, and the SQLite database and storage/ (uploads) in ~/data.",
    "The queue worker as app-worker.service and the scheduler as app-schedule.timer, both enabled.",
    "A nightly backup by app-backup.timer, with the first backup opened and checked in a scratch directory.",
    "With REPO=new, a fresh Laravel 13 app that trusts exe.dev's proxy and greets you by your exe.dev email at /hello."
  ],
  "owner_runs_after": [
    "git clone -o exe my-laravel.exe.xyz:app.git    # if you started fresh",
    "git remote add exe my-laravel.exe.xyz:app.git  # or, in your own app's checkout"
  ],
  "deployable": true,
  "deploy": {
    "new_vm_url": "https://exe-staging.dev/new?prompt=REPO%3D%3Cfill-this-in%3E%0A%0ASet%20up%20Laravel%20on%20this%20VM%20by%20following%20the%20exe.dev%20recipe%20at%20https%3A%2F%2Fexe-staging.dev%2Fdeploy%2Flaravel.md%20from%20top%20to%20bottom.%0A%0AThe%20values%20at%20the%20top%20of%20this%20message%20are%20mine%3A%0A-%20REPO%3A%20the%20GitHub%20repository%20to%20deploy%2C%20as%20owner%2Fname%20or%20a%20URL%2C%20or%20%60new%60%20to%20start%20a%20fresh%20Laravel%20app%0AAsk%20me%20for%20any%20that%20still%20say%20%3Cfill-this-in%3E%20before%20you%20need%20them.%0A%0AThe%20recipe%27s%20example%20VM%20name%20is%20my-laravel.%20This%20VM%27s%20name%20is%20the%20output%20of%20%60hostname%60%3A%20use%20it%20wherever%20the%20recipe%20writes%20my-laravel.%20Skip%20the%20step%20that%20creates%20the%20VM%3B%20you%20are%20on%20it.%0A%0AYou%20are%20done%20when%20all%20of%20these%20are%20true.%20Check%20each%20one%20yourself%3A%0A-%20PHP%208.5%2C%20Composer%20and%20Node.js%2024%20installed%3B%20nginx%20answering%20on%20port%208000%20and%20PHP-FPM%20running%20as%20app.service%2C%20both%20enabled%20to%20come%20back%20after%20a%20reboot.%0A-%20A%20~%2Fapp.git%20whose%20pre-receive%20hook%20builds%20each%20push%20into%20a%20new%20release%20%28Composer%2C%20the%20Vite%20build%2C%20migrations%2C%20optimize%29%2C%20switches%20to%20it%2C%20and%20checks%20%2Fup%3B%20a%20failed%20deploy%20rejects%20the%20push%20and%20leaves%20the%20running%20release%20alone.%0A-%20Settings%20in%20~%2Fapp.env%20%28mode%20600%29%20with%20a%20generated%20APP_KEY%2C%20and%20the%20SQLite%20database%20and%20storage%2F%20%28uploads%29%20in%20~%2Fdata.%0A-%20The%20queue%20worker%20as%20app-worker.service%20and%20the%20scheduler%20as%20app-schedule.timer%2C%20both%20enabled.%0A-%20A%20nightly%20backup%20by%20app-backup.timer%2C%20with%20the%20first%20backup%20opened%20and%20checked%20in%20a%20scratch%20directory.%0A-%20With%20REPO%3Dnew%2C%20a%20fresh%20Laravel%2013%20app%20that%20trusts%20exe.dev%27s%20proxy%20and%20greets%20you%20by%20your%20exe.dev%20email%20at%20%2Fhello.%0A%0ARead%20the%20whole%20recipe%2C%20including%20the%20closed%20details%2C%20and%20follow%20%22Install%0Aby%20hand%22%20in%20order%2C%20skipping%20VM%20creation.%20The%20walkthrough%20above%20it%20is%20for%0Athe%20owner%20afterwards.%20REPO%3Dnew%20takes%20%22Start%20a%20new%20app%22%3B%20anything%20else%0Atakes%20%22Bring%20your%20app%22%20%28turn%20URLs%20into%20owner%2Fname%29.%20If%20cloning%20fails%2C%0Athe%20repository%20is%20private%3A%20give%20the%20owner%20the%20%60ssh%20exe.dev%20integrations%0Aadd%20github%60%20command%20from%20that%20step%2C%20with%20this%20VM%27s%20name%20and%20the%0Arepository%2C%20and%20wait%20for%20them.%0ANever%20commit%20to%20the%20owner%27s%20code%2C%20on%20GitHub%20or%20in%20~%2Fapp.git%2C%20so%20their%0Afirst%20push%20fast-forwards%3A%20settings%20go%20in%20~%2Fapp.env%2C%20and%20the%20hook%20already%0Atrusts%20exe.dev%27s%20proxy.%20Generate%20secrets%3B%20ask%20the%20owner%20for%20keys%20to%0Aoutside%20services%2C%20and%20never%20invent%20values.%20If%20a%20deploy%20fails%2C%20read%20its%0Aoutput%2C%20fix%20what%20you%20can%20on%20the%20VM%2C%20and%20redeploy%20with%0A%60~%2Fapp.git%2Fhooks%2Fpre-receive%20%3C%2Fdev%2Fnull%60.%0ADon%27t%20run%20Restore%20or%20Update%2C%20don%27t%20reboot%2C%20and%20leave%20the%20VM%20private.%0AFor%20a%20new%20app%2C%20the%20URLs%20to%20open%20include%20%2Fhello.%0A%0AFinish%20with%20the%20URLs%20I%20should%20open%20and%20these%20commands%2C%20which%20only%20I%20can%20run%2C%20from%20my%20own%20computer%20%28with%20this%20VM%27s%20name%20in%20them%29%3A%0A%20%20git%20clone%20-o%20exe%20my-laravel.exe.xyz%3Aapp.git%20%20%20%20%23%20if%20you%20started%20fresh%0A%20%20git%20remote%20add%20exe%20my-laravel.exe.xyz%3Aapp.git%20%20%23%20or%2C%20in%20your%20own%20app%27s%20checkout%0A",
    "terminal": "curl -fsSL https://exe-staging.dev/deploy/laravel/prompt.txt | ssh exe.dev new --prompt=/dev/stdin",
    "prompt": "REPO=\u003cfill-this-in\u003e\n\nSet up Laravel on this VM by following the exe.dev recipe at https://exe-staging.dev/deploy/laravel.md from top to bottom.\n\nThe values at the top of this message are mine:\n- REPO: the GitHub repository to deploy, as owner/name or a URL, or `new` to start a fresh Laravel app\nAsk me for any that still say \u003cfill-this-in\u003e before you need them.\n\nThe recipe's example VM name is my-laravel. This VM's name is the output of `hostname`: use it wherever the recipe writes my-laravel. Skip the step that creates the VM; you are on it.\n\nYou are done when all of these are true. Check each one yourself:\n- PHP 8.5, Composer and Node.js 24 installed; nginx answering on port 8000 and PHP-FPM running as app.service, both enabled to come back after a reboot.\n- A ~/app.git whose pre-receive hook builds each push into a new release (Composer, the Vite build, migrations, optimize), switches to it, and checks /up; a failed deploy rejects the push and leaves the running release alone.\n- Settings in ~/app.env (mode 600) with a generated APP_KEY, and the SQLite database and storage/ (uploads) in ~/data.\n- The queue worker as app-worker.service and the scheduler as app-schedule.timer, both enabled.\n- A nightly backup by app-backup.timer, with the first backup opened and checked in a scratch directory.\n- With REPO=new, a fresh Laravel 13 app that trusts exe.dev's proxy and greets you by your exe.dev email at /hello.\n\nRead the whole recipe, including the closed details, and follow \"Install\nby hand\" in order, skipping VM creation. The walkthrough above it is for\nthe owner afterwards. REPO=new takes \"Start a new app\"; anything else\ntakes \"Bring your app\" (turn URLs into owner/name). If cloning fails,\nthe repository is private: give the owner the `ssh exe.dev integrations\nadd github` command from that step, with this VM's name and the\nrepository, and wait for them.\nNever commit to the owner's code, on GitHub or in ~/app.git, so their\nfirst push fast-forwards: settings go in ~/app.env, and the hook already\ntrusts exe.dev's proxy. Generate secrets; ask the owner for keys to\noutside services, and never invent values. If a deploy fails, read its\noutput, fix what you can on the VM, and redeploy with\n`~/app.git/hooks/pre-receive \u003c/dev/null`.\nDon't run Restore or Update, don't reboot, and leave the VM private.\nFor a new app, the URLs to open include /hello.\n\nFinish with the URLs I should open and these commands, which only I can run, from my own computer (with this VM's name in them):\n  git clone -o exe my-laravel.exe.xyz:app.git    # if you started fresh\n  git remote add exe my-laravel.exe.xyz:app.git  # or, in your own app's checkout\n",
    "prompt_url": "https://exe-staging.dev/deploy/laravel/prompt.txt"
  },
  "updated": "2026-10-17",
  "related": [
    "rails",
    "django",
    "postgres",
    "migrate-heroku",
    "docker-compose",
    "fastapi"
  ],
  "alternatives": [
    "Laravel Cloud",
    "Laravel Forge",
    "Railway"
  ]
}
