Skip to content

Deploy an API without SSH or sudo

If you’ve ever uploaded a built API through the File Manager, opened the panel terminal to run npm install, and been stopped cold — this guide is for you. That dead-end is expected, not a bug, and there’s a cleaner path that never needs the terminal at all.

The one thing to know first

The panel terminal runs as an unprivileged user and cannot use sudo. That’s a deliberate security boundary: a web terminal with root access would mean anyone who gets into your panel session owns the whole server. So you’ll never install system packages or run sudo npm install from there — and you shouldn’t have to.

Instead, let OrbixPanel install dependencies and build your app for you, server-side, with the right permissions. There are two sudo-free ways to do that:

You have…UseTerminal needed?
A Git repo (GitHub/GitLab/etc.)Deploy from Git — the panel clones, installs, builds, startsNo
A local build you upload by handApp Manager → Maintenance → Install dependenciesNo

The old habit of “upload dist/, then SSH in to npm install” is the thing we’re replacing. The Maintenance panel runs npm install, builds, and Prisma tasks for you — server-side, as the service user, with your app’s env vars loaded. You only fall back to SSH as a genuine last resort (covered at the end).


This is the fastest and most repeatable way to run an API. The panel’s agent does the privileged work for you: clone → npm ci (or npm install) → npm run build (if you have a build script) → start under systemd. Nothing runs in your terminal.

  1. Push your project to a repo. Keep package.json and package-lock.json at the repo root. You do not need to commit node_modules or dist/ — the panel builds them on the server.
  2. App Manager → Create → Deploy from Git. Paste the repository URL.
    • Public repo: URL is all you need.
    • Private repo: add a personal access token (PAT) when prompted. It’s stored encrypted, injected only for the clone, and redacted from the deploy log.
  3. Pick the branch (defaults to the repo default) and deploy.

The agent detects the runtime automatically:

  • Node (package.json): runs npm ci || npm install, then npm run build if a build script exists, then npm start — or node <entry> if there’s no start script.
  • Python (requirements.txt / Pipfile / pyproject.toml): creates a venv and installs, adding uvicorn/gunicorn if needed.
  • Go (go.mod): go build.
  • PHP (composer.json): installs via Composer and serves through php-fpm.

To ship a new version later, just Deploy again (App Manager → your app → Deploy). It re-clones, re-installs, and rebuilds — so this is your “run npm install” button. Previous deployments are kept, so you can promote/rollback.

Why this works without sudo: the deploy runs through the OrbixPanel agent, a privileged service that installs as the app’s service account (www-data) on your behalf. Your browser session never holds those privileges — the agent does, for exactly this one job.


Path B — Upload a build by hand, install from the panel

If you can’t or don’t want to use Git, you can still avoid the terminal entirely. Upload your build, then let the panel install the dependencies for you.

  1. Build locally, keeping package.json and package-lock.json at the root. You do not need to upload node_modules:

    Terminal window
    # on your machine
    npm run build # produces dist/
    myapp/
    dist/ # compiled output (e.g. dist/src/main.js)
    package.json
    package-lock.json
    prisma/ # if you use Prisma
  2. Zip the folder, upload the zip via File Manager, and Extract it in place under your app directory (e.g. /var/www/myapp). Uploading one archive is far faster than thousands of individual files.

  3. Create the app in App Manager pointing at the uploaded folder (see the next section).

  4. Install dependencies from the panel. Expand the app → Maintenance → Install dependencies. OrbixPanel runs npm ci --omit=dev (falling back to npm install) in the app directory, as the service user, and shows the output live. Click Build too if your start command needs a fresh dist/. No terminal, no sudo.

Prefer to pre-bundle instead? You can still ship node_modules inside the zip (run npm ci --omit=dev locally first) so the app runs with no install step. If you do, build on the same OS/architecture as your server (Linux x64/arm64) — otherwise native modules like bcrypt, sharp, or better-sqlite3 won’t load. When in doubt, use the Install dependencies button and let the server build them.


Create the app in OrbixPanel

Whichever path you used, the app settings are the same (Deploy from Git fills most of these in for you):

  • Name: myapp → becomes the systemd service orbixapp-myapp
  • Runtime: Node
  • Directory: the project root — /var/www/myapp (not dist/src, so relative paths like ./uploads resolve correctly and survive rebuilds)
  • Entry point: dist/src/main.js
  • Port: the port your app listens on, e.g. 5000

OrbixPanel writes a systemd unit (User=www-data, auto-restart), makes sure the app directory is owned by www-data, and starts it.

Set environment variables

Expand the app → Environment Variables → paste KEY=value lines → Save & restart. PORT is injected automatically; everything else (database URL, JWT secrets, SMTP, etc.) goes here. Values are stored encrypted and reload into the editor on refresh.

If your API uses a database, create the DB and user first and point DATABASE_URL at it. Keep the password to letters and digits — special characters like @ : / # must be percent-encoded in a connection string and are a common cause of P1000: authentication failed.

Run Prisma (generate, migrate, seed) — no SSH

Prisma is the classic case where people reach for SSH, because prisma generate, prisma migrate deploy, and the seeder all need to run on the server, against the real database, with DATABASE_URL set. The Maintenance panel does exactly that — it runs each command in the app directory with your saved env vars loaded, so Prisma connects to the same database your app uses.

Set DATABASE_URL in Environment Variables first (above), then expand the app → Maintenance → Prisma:

ButtonRunsWhen to use
Generateprisma generateAfter install, or whenever the schema changes — regenerates the Prisma Client
DB Pushprisma db pushSync the schema to the database without migration files (prototyping)
Migrateprisma migrate deployApply committed migrations to the database (production)
Seedprisma db seedRun your seeder

Each runs server-side and streams its output into the panel — no terminal, no sudo.

Order that usually works: Install dependencies → Generate → Migrate (or DB Push) → Seed → Restart.

Seeder setup: prisma db seed runs the command you declare in package.json. Point it at compiled JS so it doesn’t need ts-node on the server:

{
"prisma": { "seed": "node dist/prisma/seed.js" }
}

If your seed is TypeScript (prisma/seed.ts), compile it as part of your build, or set the seed command to "tsx prisma/seed.ts" and include tsx in your dependencies — otherwise the server has no TypeScript runtime and the seed fails with Cannot find module './seed.ts'.

Attach the domain + SSL

  1. Point DNS: an A record api.example.com → <server IP>.
  2. App Manager → your app → Domain → api.example.com → Save. OrbixPanel writes an nginx reverse-proxy vhost pointing the domain at your app’s port.
  3. Request SSL on the app. certbot issues the certificate and wires it into the vhost.

Verify:

Terminal window
curl -s https://api.example.com/health; echo

(Run that from your own machine — not the panel terminal.)


Last resort — the SSH path

If you’re doing something the panel doesn’t support yet (a custom build step, system-level tooling), SSH into the server as a user with sudo and run the privileged commands there:

Terminal window
cd /var/www/myapp
sudo -u www-data npm ci --omit=dev # install AS the service user
sudo chown -R www-data:www-data /var/www/myapp # if you installed as root by mistake

Installing as www-data matters: if you install as root, the app (which runs as www-data) can’t write to node_modules or create runtime folders, and you’ll see EACCES errors. This is precisely the manual bookkeeping that Path A and Path B remove — reach for SSH only when you truly need it.


Quick error → fix reference

ErrorCauseFix
Can’t run npm install in the panel terminalThe terminal has no sudo, by designUse Maintenance → Install dependencies, or Deploy from Git — don’t install from the terminal
Cannot find module '@nestjs/core'Dependencies not presentMaintenance → Install dependencies, or Deploy from Git
Prisma Cannot find module './seed.ts'No TypeScript runtime on the serverPoint prisma.seed at compiled JS, or use tsx and add it to dependencies
EACCES: permission denied, mkdir 'uploads/…'App dir not owned by www-dataDeploy from Git (handles ownership), or sudo chown -R www-data:www-data /var/www/myapp over SSH
Prisma P1000DB user/password/name missing or mismatchedCreate the DB + user; check DATABASE_URL encoding
“Start request repeated too quickly”A crash-loop tripped systemd’s limitThe panel resets this for you on Deploy/Restart; over SSH it’s sudo systemctl reset-failed orbixapp-myapp
Native module fails to load after a hand-uploadnode_modules built on a different OS/archRebuild on matching Linux arch, or use Deploy from Git

See the App Deployment reference for the full feature-by-feature documentation.