A fresh VPS is a blank Linux machine with a root login. Before any application goes on it, it needs a non-root user, key-only SSH, a firewall, automatic security updates, a reverse proxy with HTTPS and a way to keep your app running. This guide sets up that baseline once on Ubuntu 24.04 LTS, then points you to a dedicated guide for each popular stack.
Everything here needs root access and applies to a VPS or server you manage yourself, with no control panel. On shared hosting you cannot change server configuration; use the panel's Node.js and Python tools or the App Platform instead (section 8).
Create a sudo user with an SSH key, turn off password and root password logins, enable UFW (SSH, 80, 443), Fail2ban and unattended security upgrades. Put Nginx with Certbot, or Caddy, in front of your app, run the app as a systemd service bound to 127.0.0.1, and keep databases on localhost. Then follow the stack guide for Node.js, Python, PHP, Java, Go or Rust, and set up off-server backups before launch.
1. Before you start
You need the VPS IP address, the root password or key from your welcome details, and a domain whose A record points to the VPS (and AAAA for IPv6 if you use it). The commands assume Ubuntu 24.04 LTS; Debian 12 is almost identical. On AlmaLinux or Rocky Linux, use dnf instead of apt and firewalld instead of UFW.
Avoid end-of-life releases for new servers. CentOS 7 and CentOS 8 no longer receive updates, and Ubuntu 20.04 is out of standard support.
2. Create a user and log in with a key
Work as a normal user with sudo, not as root. From your own computer, create a key if you do not have one: ssh-keygen -t ed25519. Then on the server, as root:
adduser deploy
usermod -aG sudo deploy
mkdir -p /home/deploy/.ssh
nano /home/deploy/.ssh/authorized_keys # paste your public key (.pub)
chmod 700 /home/deploy/.ssh && chmod 600 /home/deploy/.ssh/authorized_keys
chown -R deploy:deploy /home/deploy/.sshOpen a second terminal and confirm ssh [email protected] works and that sudo -v accepts your password. Only then harden SSH with a drop-in file, which survives package updates better than editing the main config:
sudo tee /etc/ssh/sshd_config.d/99-hardening.conf >/dev/null <<'EOF'
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin prohibit-password
EOF
sudo sshd -t && sudo systemctl reload sshKeep your first session open until a fresh login succeeds. More options are in the SSH security hardening checklist.
3. Firewall, Fail2ban and automatic updates
sudo apt update && sudo apt -y upgrade
sudo apt -y install ufw fail2ban unattended-upgrades curl git ca-certificates
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo systemctl enable --now fail2ban
sudo dpkg-reconfigure --priority=low unattended-upgradesAllow SSH before enabling UFW, or you lock yourself out. Fail2ban's default sshd jail is enough to start. Open other ports only when a service really needs to be public; databases and admin dashboards should not be. Details: setting up a firewall on your VPS.
Ports published with docker run -p 5432:5432 are opened in iptables by Docker itself and are reachable from the internet even when UFW says they are closed. Publish container ports on localhost only, for example -p 127.0.0.1:5432:5432, and let the reverse proxy be the only public entry point.
4. Reverse proxy and HTTPS
Your app listens on a local port such as 3000 or 8000; a reverse proxy on 80 and 443 handles TLS and forwards requests. Choose one:
| Option | Best for | HTTPS |
|---|---|---|
| Nginx + Certbot | Maximum control, most examples online, static files and caching | Certbot obtains and renews Let's Encrypt certificates |
| Caddy | The simplest setup, a few lines per site | Automatic, built in |
Nginx. Install with sudo apt -y install nginx certbot python3-certbot-nginx, then create /etc/nginx/sites-available/example.com:
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
sudo certbot --nginx -d example.com -d www.example.com --redirectCertbot adds the 443 server block and the redirect, and installs a renewal timer. Test renewal with sudo certbot renew --dry-run.
Caddy. After installing it from the official Caddy repository, the whole site is:
example.com, www.example.com {
reverse_proxy 127.0.0.1:3000
}Put that in /etc/caddy/Caddyfile and run sudo systemctl reload caddy. For a deeper comparison, read Nginx vs Caddy for app gateways.
5. Keep the app running with systemd
A systemd unit starts your app at boot, restarts it if it crashes and sends its output to the journal. Create /etc/systemd/system/myapp.service:
[Unit]
Description=My app
After=network-online.target
Wants=network-online.target
[Service]
User=deploy
WorkingDirectory=/srv/myapp
EnvironmentFile=/srv/myapp/.env
ExecStart=/srv/myapp/start.sh
Restart=on-failure
RestartSec=3
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=full
[Install]
WantedBy=multi-user.targetsudo systemctl daemon-reload
sudo systemctl enable --now myapp
journalctl -u myapp -fMake the app listen on 127.0.0.1, not 0.0.0.0, so only the proxy can reach it. Keep secrets in the .env file with chmod 600, never in the repository.
6. Your stack
The baseline above is the same for every language. Each stack then has its own runtime, process model and build step. Use the dedicated guide:
| Stack | Typical process setup | Guide |
|---|---|---|
| Node.js (Express, Next.js, NestJS) | Node LTS, run with systemd or PM2 behind the proxy | MERN/MEAN on a clean VPS |
| Python (FastAPI, Django, Flask) | Virtualenv, Gunicorn with Uvicorn workers, systemd | FastAPI production deployment |
| Java (Spring Boot) | JDK LTS, runnable jar as a systemd service | Spring Boot on a VPS |
| Go | Single static binary, systemd | Go applications on a VPS |
| Rust (Actix, Axum) | Release build, systemd | Rust web applications on a VPS |
| PHP (Laravel, WordPress) | PHP-FPM behind Nginx, or a panel | Section 8 |
For PHP, Nginx talks to PHP-FPM through fastcgi_pass unix:/run/php/php8.3-fpm.sock; (adjust to the version you install). Use the PHP version your distribution or a maintained repository supports, and keep it updated.
For deployments, build and test in CI and copy only the result to the server. GitHub Actions CI/CD to a VPS shows a complete pipeline with a restricted deploy key.
7. Databases, monitoring and backups
Databases. Install PostgreSQL, MySQL/MariaDB or Redis from the distribution packages and keep them on localhost: listen_addresses = 'localhost' for PostgreSQL, bind-address = 127.0.0.1 for MySQL, and bind 127.0.0.1 with a password for Redis. To manage them from your computer, use an SSH tunnel: ssh -L 5432:127.0.0.1:5432 [email protected]. For MongoDB, see installing MongoDB on a VPS.
Monitoring. Start with journalctl, htop and df -h. For metrics, dashboards and alerts, read Prometheus, Grafana and Loki on a VPS.
Backups. Back up the database with pg_dump or mysqldump and your app's uploads on a schedule, and copy them off the server to object storage or another machine. Test a restore before you need one. A snapshot of the same VPS is not a substitute for an off-site copy.
- One non-root user per server, SSH keys only
- App on 127.0.0.1, proxy as the only public entry
- Security updates applied automatically
- Off-server backups with a tested restore
- Running the app as root
- Database port open to the internet
- Container ports published on 0.0.0.0 past UFW
- Secrets committed to Git
8. Where Domain India fits
A Domain India VPS gives you full root access and is self-managed by default: you run the operating system, firewall and software, as in this guide. At checkout you can choose no panel, or a panel such as CyberPanel, Webuzo or DirectAdmin, as listed on the VPS page.
- 1 vCPU
- 2 GB DDR4 RAM
- 64 GB NVMe SSD Storage
- 2 TB Monthly Bandwidth
- 2 vCPU
- 4 GB DDR4 RAM
- 128 GB NVMe SSD Storage
- 3 TB Monthly Bandwidth
If you would rather not manage a server at all:
- App Platform runs your app for you, with a PostgreSQL database and automatic SSL. Node.js is detected automatically; other stacks need a Dockerfile. See Getting Started with App Platform.
- Shared hosting runs PHP sites, and Node.js or Python apps through the panel's application tools. See deploying a Node.js app on shared hosting.
Prices on the cards are Domain India list prices and exclude 18% GST.
Frequently asked questions
Which Linux distribution should I use for a new VPS?
Use a current long-term-support release such as Ubuntu 24.04 LTS, Debian 12, or AlmaLinux or Rocky Linux 9. Avoid end-of-life releases such as CentOS 7 and CentOS 8, which no longer receive security updates.
Should I disable root login over SSH?
Disable password logins for every user and use SSH keys. Setting PermitRootLogin to prohibit-password allows root only with a key; many administrators go further and set it to no, working as a sudo user instead. Always confirm a new key login works in a second terminal before reloading SSH.
Nginx or Caddy for my reverse proxy?
Caddy is simpler and obtains and renews HTTPS certificates automatically. Nginx gives more control and has more examples online, and uses Certbot for Let's Encrypt certificates. Both are good production choices for a single VPS.
Why is my database reachable from the internet when UFW blocks the port?
Usually because it runs in Docker and the port was published on all interfaces. Docker writes its own iptables rules that bypass UFW. Publish the port on 127.0.0.1 only, or do not publish it and connect over a Docker network.
Is a Domain India VPS managed?
By default a Domain India VPS is self-managed, with full root access, so you install and maintain the software yourself. The VPS page lists the control panel options you can choose at checkout.
Can I run these stacks without a VPS?
Often, yes. The Domain India App Platform runs Node.js apps, and other stacks through a Dockerfile, with a PostgreSQL database and automatic SSL, and shared hosting runs PHP sites and Node.js or Python apps through the control panel.
Ready to build? Pick a plan on the VPS page, follow sections 2 to 5 once, then open the guide for your stack. If you are unsure whether you need a server at all, compare it with the App Platform, or open a ticket and tell us what you want to run.
Full root access on KVM, with your choice of Linux distribution and an optional control panel.
Compare VPS plans