Application deployment on a Linux VM is the process of moving an application from source code into a repeatable production runtime with the correct dependencies, configuration, process supervision, networking, TLS, observability, and rollback path.
For a small team, the reliable pattern is usually:
source code
→ build or package
→ deploy artifact
→ configure environment/secrets
→ start under a process manager
→ expose through a reverse proxy
→ terminate TLS
→ verify health
→ monitor logs
→ retain a rollback target
Raff Technologies provides cloud VMs, private networking, volumes, object storage, managed databases, and deployment-adjacent services, but the guest operating system and the application running inside a VM remain the customer's responsibility. This guide explains the deployment architecture and decision points. Framework-specific commands belong in the linked tutorials.
Application deployment is more than copying code to a server
A development application can often start with one command. A production application needs a controlled operating model.
A production deployment should answer at least these questions:
- What exact code or artifact is being released?
- Where are runtime dependencies installed?
- Where do environment variables and secrets live?
- What keeps the application running after an SSH session ends?
- What receives public HTTP or HTTPS traffic?
- How is TLS renewed?
- What happens when the process crashes?
- How do you know the release is healthy?
- How do you roll back?
- Which data survives a rebuild?
If those answers exist only in one engineer's terminal history, the workload is not deployment-ready.
A practical Linux VM deployment architecture
For many small applications, one VM is enough at the beginning.
A common layout is:
Internet
↓
DNS
↓
Nginx or Caddy
↓
Application process
↓
Database / cache / object storage
The reverse proxy owns the public HTTP entry point. The application listens on a private local port such as 127.0.0.1:3000 or 127.0.0.1:8000. The database should not be exposed to the public internet unless there is a specific, secured reason.
That separation gives you a clean boundary:
- the reverse proxy handles HTTP routing and TLS;
- the application process handles application logic;
- a process manager keeps the application alive;
- the data layer is managed separately;
- monitoring checks the system from outside the process.
For a broader server baseline before application deployment, use VPS Setup Checklist: What to Configure After Deployment.
Choose what you deploy: source, build artifact, or container
There are three common VM deployment models.
| Model | How it works | Good fit | Main trade-off |
|---|
| Source deployment | Clone/pull source on the VM and build there | Very small teams, simple apps | Production server becomes part of the build system |
| Artifact deployment | CI creates a versioned build, archive, binary, or package and the VM receives it | Repeatable VM releases | Requires a build pipeline and artifact discipline |
| Container deployment | Build an image, then run the versioned image on the VM | Docker-based applications, reproducible dependencies | Adds image registry/container operations |
For production, versioned artifacts or images are usually easier to reason about than an untracked git pull. They let you identify exactly what was released and preserve the previous version for rollback.
That does not mean every small team needs Kubernetes. A single VM running systemd, PM2, Gunicorn, or Docker Compose can be a valid production architecture when the workload fits.
If you are evaluating containers specifically, see Docker on a Cloud VM: Architecture, Security, and Operations.
Keep build and runtime responsibilities separate
A production VM should contain only what the application needs to run whenever practical.
For example:
- a Node.js application may build assets in CI and deploy the output plus runtime dependencies;
- a Next.js application may build a production bundle and run it under a process manager;
- a Python application may install a locked dependency set into a virtual environment;
- a Laravel application may deploy application code, install production Composer dependencies, run migrations carefully, and restart workers;
- a containerized application may build an immutable image outside the server and pull that version during deployment.
The more deterministic the build output is, the easier it is to reproduce and roll back.
Avoid a deployment flow where production success depends on whatever package versions happen to be available when a human SSHs into the server.
Environment variables and secrets belong outside source control
Configuration changes between development, staging, and production.
Typical production configuration includes database connection strings, API keys, application secrets, email credentials, object-storage credentials, hostnames, environment names, and feature switches.
Do not hard-code production secrets into the repository.
On a straightforward VM deployment, environment-specific configuration may be loaded through a protected environment file, systemd environment configuration, a deployment platform's secret store, or another secrets-management mechanism. Restrict file permissions and ensure deployment logs do not print secret values.
The important architectural rule is: the artifact should be deployable without embedding the production secret set inside it.
Use a process manager instead of an SSH session
A production application must survive logout, process crashes, VM restarts, deployments, and operator mistakes.
That is the job of a service/process manager.
| Runtime | Common supervisor |
|---|
| Generic Linux service | systemd |
| Node.js | systemd or PM2 |
| Python WSGI/ASGI | systemd supervising Gunicorn/Uvicorn |
| Container workload | Docker or Docker Compose restart policies |
The supervisor should define the command, working directory, restart behavior, user, environment, and startup ordering.
Do not use nohup, screen, or a manually opened terminal as the production lifecycle manager.
For a Node.js example, see How to Install PM2 and Deploy Node.js on Ubuntu 24.04.
Put a reverse proxy in front of the application
Most VM-hosted web applications should not expose their framework server directly on ports 80 or 443.
A reverse proxy can provide public HTTP/HTTPS entry, TLS termination, hostname routing, redirects, forwarding headers, timeouts, compression, access logs, and routing to multiple local services.
Nginx and Caddy are both viable. Caddy has a simple automatic-HTTPS workflow when a public hostname points to the server, while Nginx gives operators a mature and explicit configuration model.
Use Caddy Server vs Nginx: Performance, HTTPS & Reverse Proxy for that decision.
Treat TLS and DNS as part of deployment
A deployment is not complete merely because curl localhost:3000 works.
The public path should also be verified:
DNS → public IP → firewall → reverse proxy → application
Check that DNS resolves to the intended server, ports 80/443 are intentionally exposed, the application port is not unintentionally public, TLS is valid, HTTP redirects work, and the reverse proxy forwards the expected host/protocol information.
For Nginx with Let's Encrypt, see How to Install Certbot for Nginx on Ubuntu 24.04.
Add health checks before automating releases
A deployment pipeline should be able to answer a binary question: did the new version become healthy?
At minimum, distinguish:
- process alive;
- application ready;
- external synthetic check.
Do not declare deployment success immediately after the process starts. Give the application time to initialize and verify the public path.
Make rollback a deployment requirement
A release process that can deploy but cannot roll back is incomplete.
A simple artifact-based layout can look like:
/opt/app/releases/
2026-09-20-abc123/
2026-09-22-def456/
/opt/app/current -> /opt/app/releases/2026-09-22-def456/
A rollback changes the active release to the previous known-good version and restarts the service.
The difficult part is usually the database. Application rollback and database rollback are not automatically equivalent. Schema changes should be designed so the previous application version can still operate when practical, or the migration needs an explicit reversal plan.
For release-strategy trade-offs, see Blue-Green vs Rolling Deployments: Cost and Rollback.
Keep persistent data outside the disposable application path
A deploy should not overwrite important data.
Disposable data includes application binaries, generated build output, dependency directories, container images, and temporary files.
Persistent data includes database data, user uploads, durable queues, and application-generated files that must survive rebuilds.
Persistent data may belong in a managed database, attached volume, or object storage depending on access patterns.
For application uploads, see App Uploads: VM Disk vs Object Storage.
Choose a VM size from measured workload needs
A deployment guide should not invent a universal server size.
Start from runtime memory, build memory if builds happen on the VM, concurrent request load, background workers, database placement, log volume, and local storage growth.
Use VM Sizing Guide: How to Choose CPU, RAM, Storage & VM Size rather than copying a tutorial test VM into production without measurement.
Framework-specific deployment paths
This page owns the architecture. The tutorials own exact implementation.
The repeated pattern is:
package → configure → supervise → proxy → secure → verify → observe → roll back
When a VM is not the simplest deployment target
A VM is attractive when you need full operating-system control, arbitrary processes, persistent local services, custom networking, predictable long-running compute, or direct runtime access.
An app platform can be a better fit when the team primarily wants Git-based builds, managed runtime lifecycle, automatic deployment primitives, and less server administration.
Compare those models in Cloud VM vs App Platform: What Startups Should Know.
Application deployment checklist
Before calling a VM deployment production-ready, confirm:
- the deployed version is identifiable;
- dependencies are locked;
- secrets are outside source control;
- the process runs under a supervisor;
- the application does not depend on an interactive SSH session;
- the public path uses a reverse proxy;
- TLS is valid;
- only required ports are public;
- health checks pass;
- logs are available;
- persistent data has a recovery plan;
- a previous version can be restored;
- deployment steps are documented or automated.
Frequently asked questions
What is application deployment?
Application deployment is moving a versioned application into a runtime environment and making it safely available to users. On a Linux VM this includes packaging, configuration, process supervision, networking, TLS, verification, and rollback.
Should I build an application directly on the production VM?
You can, but external builds or versioned artifacts are easier to reproduce. Building on production couples release success to the production machine's local state and toolchain.
Do I need Nginx or Caddy in front of every application?
Not every workload needs a reverse proxy, but most public web applications benefit from one for TLS termination, hostname routing, headers, timeouts, and access logging.
Is PM2 required for Node.js deployment?
No. PM2 is one option. systemd can also supervise Node.js directly. The important requirement is managed application lifecycle independent of an interactive terminal.
Do I need Docker to deploy on a VPS?
No. Native runtime deployments, systemd-managed services, PM2, Gunicorn, PHP-FPM, and containers can all be valid.
How should I roll back a VM deployment?
Keep a previous known-good artifact or container image, switch the active release back, restart the service, and verify health. Database changes need separate compatibility or rollback planning.
Use an app platform when managed builds and runtime lifecycle matter more than OS control. Use a VM when you need direct control over processes, packages, storage, networking, or long-running services.
Sources