{
  "manifest_version": "1",
  "slug": "nextjs",
  "title": "Next.js",
  "headline": "Deploy Next.js on your own server",
  "description": "Run a Next.js app on an exe.dev VM: git push to deploy, HTTPS, Server Actions, ISR and image optimization, SQLite on a persistent disk, nightly backups.",
  "category": "frameworks",
  "tags": [
    "nextjs",
    "react",
    "self-hosting",
    "vercel alternative",
    "git push deploy",
    "sqlite",
    "drizzle",
    "systemd"
  ],
  "url": "https://exe.dev/deploy/nextjs",
  "markdown_url": "https://exe.dev/deploy/nextjs.md",
  "upstream": {
    "website": "https://nextjs.org",
    "source": "https://github.com/vercel/next.js",
    "docs": "https://nextjs.org/docs",
    "license": "MIT"
  },
  "example_vm_name": "my-nextjs",
  "time": "15 minutes",
  "stack": [
    {
      "name": "Next.js 16",
      "detail": "next start, as app.service under systemd",
      "port": 8000,
      "data": "~/app → the current release in ~/releases"
    },
    {
      "name": "Node.js 24 LTS",
      "detail": "NodeSource's apt package; npm, pnpm, and Yarn"
    },
    {
      "name": "git push deploys",
      "detail": "A pre-receive hook in ~/app.git builds, migrates, and switches"
    },
    {
      "name": "SQLite",
      "detail": "Drizzle ORM in new apps; settings in ~/app.env",
      "data": "~/data"
    },
    {
      "name": "Nightly backup",
      "detail": "app-backup.timer; data, settings, and the repository",
      "data": "~/backups"
    }
  ],
  "access": [
    {
      "url": "https://my-nextjs.exe.xyz/",
      "who": "private",
      "what": "Your app, until you make it public"
    }
  ],
  "inputs": [
    {
      "name": "REPO",
      "description": "the GitHub repository to deploy, as owner/name or a URL, or `new` to start a fresh Next.js app"
    }
  ],
  "delivers": [
    "Node.js 24 and the app answering on port 8000 as app.service, enabled to come back after a reboot.",
    "A ~/app.git repository whose pre-receive hook deploys each push to main into a new release and rejects the push if the deploy fails.",
    "Settings and secrets in ~/app.env (mode 600), read by the build and by the server.",
    "Data in ~/data, backed up nightly by app-backup.timer, with the newest backup opened and checked in a scratch directory.",
    "With REPO=new, a fresh create-next-app project whose /guestbook page saves messages to SQLite."
  ],
  "deployable": true,
  "deploy": {
    "new_vm_url": "https://exe.dev/new?prompt=REPO%3D%3Cfill-this-in%3E%0A%0ASet%20up%20Next.js%20on%20this%20VM%20by%20following%20the%20exe.dev%20recipe%20at%20https%3A%2F%2Fexe.dev%2Fdeploy%2Fnextjs.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%20Next.js%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-nextjs.%20This%20VM%27s%20name%20is%20the%20output%20of%20%60hostname%60%3A%20use%20it%20wherever%20the%20recipe%20writes%20my-nextjs.%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-%20Node.js%2024%20and%20the%20app%20answering%20on%20port%208000%20as%20app.service%2C%20enabled%20to%20come%20back%20after%20a%20reboot.%0A-%20A%20~%2Fapp.git%20repository%20whose%20pre-receive%20hook%20deploys%20each%20push%20to%20main%20into%20a%20new%20release%20and%20rejects%20the%20push%20if%20the%20deploy%20fails.%0A-%20Settings%20and%20secrets%20in%20~%2Fapp.env%20%28mode%20600%29%2C%20read%20by%20the%20build%20and%20by%20the%20server.%0A-%20Data%20in%20~%2Fdata%2C%20backed%20up%20nightly%20by%20app-backup.timer%2C%20with%20the%20newest%20backup%20opened%20and%20checked%20in%20a%20scratch%20directory.%0A-%20With%20REPO%3Dnew%2C%20a%20fresh%20create-next-app%20project%20whose%20%2Fguestbook%20page%20saves%20messages%20to%20SQLite.%0A%0ARead%20the%20whole%20recipe%2C%20including%20the%20closed%20details.%20Follow%20%22Set%20up%20by%0Ahand%22%20from%20%22Install%20Node.js%22%20to%20the%20end%20of%20it%2C%20as%20exedev%3B%20the%20walkthrough%0Aabove%20it%20is%20for%20the%20owner%20afterwards.%20Do%20not%20run%20Restore%20or%20Update%2C%20and%0Ado%20not%20reboot.%20Leave%20public%20sharing%20off.%0AREPO%20picks%20one%20path%20in%20%22Get%20the%20code%22%3A%20%60new%60%20starts%20a%20fresh%20app%3B%0Aanything%20else%20is%20a%20GitHub%20repository%20%28turn%20URLs%20into%20owner%2Fname%29.%20Never%0Acommit%20to%20a%20repository%20you%20cloned%3A%20its%20settings%20go%20in%20~%2Fapp.env.%20If%20a%0Aclone%20fails%2C%20follow%20the%20private-repository%20steps%2C%20and%20if%20that%20fails%0Atoo%2C%20give%20the%20owner%20the%20integration%20command%20and%20wait%20for%20them.%0AFor%20a%20cloned%20app%2C%20fill%20~%2Fapp.env%20from%20its%20.env.example%20%28or%20README%29%3A%0Agenerate%20secrets%20with%20openssl%2C%20ask%20the%20owner%20for%20keys%20to%20outside%0Aservices%2C%20and%20never%20invent%20values.%20If%20a%20build%20fails%2C%20read%20its%20output%2C%0Afix%20what%20you%20can%20without%20editing%20the%20code%2C%20and%20redeploy%20with%0A~%2Fapp.git%2Fhooks%2Fpre-receive%20HEAD.%0AFinish%20with%20the%20app%27s%20URL%20%28and%20%2Fguestbook%20for%20a%20new%20app%29%20and%20the%20git%0Acommands%20the%20owner%20runs%20on%20their%20own%20computer%2C%20from%20%22Deploy%20with%20git%0Apush%22%3A%20%60git%20clone%20-o%20exe%60%20for%20a%20new%20app%2C%20%60git%20remote%20add%20exe%60%20for%20a%0AGitHub%20repository%2C%20then%20%60git%20push%20exe%20main%60%20%28or%20their%20default%20branch%29.%0A%0AFinish%20with%20the%20URLs%20I%20should%20open.%0A",
    "terminal": "curl -fsSL https://exe.dev/deploy/nextjs/prompt.txt | ssh exe.dev new --prompt=/dev/stdin",
    "prompt": "REPO=\u003cfill-this-in\u003e\n\nSet up Next.js on this VM by following the exe.dev recipe at https://exe.dev/deploy/nextjs.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 Next.js 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-nextjs. This VM's name is the output of `hostname`: use it wherever the recipe writes my-nextjs. 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- Node.js 24 and the app answering on port 8000 as app.service, enabled to come back after a reboot.\n- A ~/app.git repository whose pre-receive hook deploys each push to main into a new release and rejects the push if the deploy fails.\n- Settings and secrets in ~/app.env (mode 600), read by the build and by the server.\n- Data in ~/data, backed up nightly by app-backup.timer, with the newest backup opened and checked in a scratch directory.\n- With REPO=new, a fresh create-next-app project whose /guestbook page saves messages to SQLite.\n\nRead the whole recipe, including the closed details. Follow \"Set up by\nhand\" from \"Install Node.js\" to the end of it, as exedev; the walkthrough\nabove it is for the owner afterwards. Do not run Restore or Update, and\ndo not reboot. Leave public sharing off.\nREPO picks one path in \"Get the code\": `new` starts a fresh app;\nanything else is a GitHub repository (turn URLs into owner/name). Never\ncommit to a repository you cloned: its settings go in ~/app.env. If a\nclone fails, follow the private-repository steps, and if that fails\ntoo, give the owner the integration command and wait for them.\nFor a cloned app, fill ~/app.env from its .env.example (or README):\ngenerate secrets with openssl, ask the owner for keys to outside\nservices, and never invent values. If a build fails, read its output,\nfix what you can without editing the code, and redeploy with\n~/app.git/hooks/pre-receive HEAD.\nFinish with the app's URL (and /guestbook for a new app) and the git\ncommands the owner runs on their own computer, from \"Deploy with git\npush\": `git clone -o exe` for a new app, `git remote add exe` for a\nGitHub repository, then `git push exe main` (or their default branch).\n\nFinish with the URLs I should open.\n",
    "prompt_url": "https://exe.dev/deploy/nextjs/prompt.txt"
  },
  "updated": "2026-10-17",
  "related": [
    "postgres",
    "fastapi",
    "dev-test-prod",
    "claude-code",
    "django",
    "docker-compose"
  ],
  "alternatives": [
    "Vercel",
    "Netlify",
    "Railway"
  ]
}
