Skip to content
Back to Blog
Linux & Server11 min read

VPS Hosting Setup: 7 Steps to Deploy Your First Server

Walk through the essential steps to configure a new VPS, from generating SSH keys and hardening access to installing a web stack and deploying your first application.

Written by Abdul AbrorTechnical Hosting Support Engineer
VPS Hosting Setup: 7 Steps to Deploy Your First Server
On this page

You've just spun up a fresh VPS from your provider. The dashboard says it's running, and you've got a root password in your inbox. Now what?

A raw server is like an unlocked house with no furniture—you need to secure the doors, set up rooms, and move in your stuff before anyone else does. I'll walk you through the seven steps I follow every time I provision a new VPS, from locking down SSH to deploying a live application. These aren't theoretical best practices; they're the exact sequence that keeps support tickets from piling up later.

Step 1: Generate and Configure SSH Keys

Password authentication is the first thing attackers try. SSH keys are faster, more secure, and let you skip typing passwords every time you connect.

On your local machine (Linux, macOS, or WSL), open a terminal and run:

ssh-keygen -t ed25519 -C "[email protected]"

Press Enter to accept the default file location (~/.ssh/id_ed25519). Set a passphrase when prompted—it's optional but adds a second layer if someone steals your private key. You'll see two files: id_ed25519 (private, keep this safe) and id_ed25519.pub (public, this goes on the server).

If you're on an older system that doesn't support Ed25519, fall back to RSA with at least 4096 bits:

ssh-keygen -t rsa -b 4096 -C "[email protected]"

Now copy the public key to your VPS. Replace your_server_ip with the actual IP:

ssh-copy-id root@your_server_ip

Enter the root password when prompted. The utility appends your public key to ~/.ssh/authorized_keys on the server. Test the connection:

ssh root@your_server_ip

If you log in without typing a password, you're set. If not, double-check the key was added to ~/.ssh/authorized_keys and that the file permissions are 600.

Step 2: Create a Non-Root User with Sudo Access

Running everything as root is risky. One typo in a script can wipe system files. Create a regular user and grant it sudo privileges for administrative tasks.

Log in as root and add a new user (replace deploy with your preferred username):

adduser deploy

You'll be prompted for a password and optional details. Then add the user to the sudo group:

usermod -aG sudo deploy

On some distributions the group is called wheel instead of sudo—check with groups deploy after running the command. Now copy your SSH key to the new user's home directory:

mkdir -p /home/deploy/.ssh
cp /root/.ssh/authorized_keys /home/deploy/.ssh/
chown -R deploy:deploy /home/deploy/.ssh
chmod 700 /home/deploy/.ssh
chmod 600 /home/deploy/.ssh/authorized_keys

Test the new user in a separate terminal window (don't close your root session yet):

ssh deploy@your_server_ip
sudo ls /root

Enter the user's password when sudo prompts. If you see the contents of /root, sudo is working. From now on, use this account instead of root.

Step 3: Harden SSH Configuration

Default SSH settings allow password login on port 22, which is convenient for attackers. Disable password authentication and optionally move SSH to a non-standard port.

Edit the SSH daemon config:

sudo nano /etc/ssh/sshd_config

Find or add these lines:

PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
Port 2222

Changing the port to something other than 22 won't stop determined attackers, but it drops the noise from automated scanners. Pick a number above 1024 that's not already in use (check with sudo ss -tulpn | grep :2222). Just remember to update your firewall rules and SSH client config.

Save and close the file, then restart SSH:

sudo systemctl restart sshd

Do NOT close your current session yet. Open a new terminal and test the new settings:

ssh -p 2222 deploy@your_server_ip

If you're locked out, go back to your original root session and revert the changes. Once you confirm the new config works, you can safely close the root session.

Step 4: Configure the Firewall

An open VPS listens on every port by default. Use ufw (Uncomplicated Firewall) to block everything except the services you need.

Install ufw if it's not already present:

sudo apt update
sudo apt install ufw

Before enabling the firewall, allow SSH on your chosen port (if you kept it at 22, adjust accordingly):

sudo ufw allow 2222/tcp

If you skip this step and enable ufw, you'll lock yourself out. Add rules for HTTP and HTTPS if you're running a web server:

sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

Now enable the firewall:

sudo ufw enable

Confirm the rules:

sudo ufw status

You should see your SSH port, 80, and 443 listed as ALLOW. For database servers, mail relays, or custom applications, add rules for those ports as needed. Keep the default policy to deny all incoming connections; explicitly open only what you use.

Step 5: Update the System and Install Essential Packages

Distribution images are often weeks or months old. Update everything before you install applications.

On Debian or Ubuntu:

sudo apt update
sudo apt upgrade -y

On CentOS, Rocky, or AlmaLinux:

sudo dnf upgrade -y

The -y flag skips confirmation prompts. If a kernel update is included, reboot afterward:

sudo reboot

Wait a minute, then reconnect. Now install baseline utilities you'll need for troubleshooting and building software:

sudo apt install -y curl wget git vim htop build-essential

Adjust the package names for your distribution if needed. build-essential on Debian/Ubuntu provides gcc, make, and other compilation tools; on RHEL-based systems, use sudo dnf groupinstall "Development Tools".

Step 6: Install a Web Stack (Nginx, Database, Runtime)

Your application needs somewhere to run. I'll use Nginx, PostgreSQL, and Node.js as an example, but swap in Apache, MySQL, PHP, Python, or whatever your stack requires.

Install Nginx:

sudo apt install -y nginx
sudo systemctl enable nginx
sudo systemctl start nginx

Visit http://your_server_ip in a browser. You should see the default Nginx welcome page. If you don't, check the firewall (ufw port 80) and Nginx logs (sudo journalctl -u nginx).

Install PostgreSQL:

sudo apt install -y postgresql postgresql-contrib
sudo systemctl enable postgresql
sudo systemctl start postgresql

Switch to the postgres user and create a database and user for your application:

sudo -u postgres psql

Inside the psql prompt:

CREATE DATABASE myapp;
CREATE USER myappuser WITH PASSWORD 'secure_password_here';
GRANT ALL PRIVILEGES ON DATABASE myapp TO myappuser;
\q

Replace myapp, myappuser, and the password with your actual values. For Node.js, install it via NodeSource or use a version manager like nvm. Here's the NodeSource approach for the LTS version:

curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash -
sudo apt install -y nodejs
node --version

If you see a version number, Node is installed. For production workloads, also install a process manager like PM2:

sudo npm install -g pm2

Step 7: Deploy and Run Your Application

Clone your application repository, install dependencies, configure environment variables, and start the service. I'll assume a Node.js app, but the pattern is similar for other runtimes.

Clone your repo (or upload files via scp/rsync):

cd /home/deploy
git clone https://github.com/yourusername/your-app.git
cd your-app

Install dependencies:

npm install --production

Create a .env file for secrets and config:

nano .env

Add your database credentials, API keys, and any other environment variables:

DATABASE_URL=postgresql://myappuser:secure_password_here@localhost/myapp
NODE_ENV=production
PORT=3000

Save and close. Start the app with PM2:

pm2 start npm --name "myapp" -- start
pm2 save
pm2 startup

The pm2 startup command prints a line you need to run with sudo—copy and execute it. This ensures PM2 restarts your app after a reboot.

Configure Nginx as a reverse proxy. Create a new site config:

sudo nano /etc/nginx/sites-available/myapp

Paste this template (adjust the domain and port):

server {
    listen 80;
    server_name yourdomain.com www.yourdomain.com;

    location / {
        proxy_pass http://localhost:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
}

Enable the site and reload Nginx:

sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

Point your domain's A record to the VPS IP, wait for DNS to propagate (usually a few minutes), then visit your domain. You should see your app. If you get a 502 Bad Gateway, the app isn't listening on the port Nginx expects—check pm2 logs and the .env file.

What happens when SSH keys stop working?

In support tickets I've handled, the usual culprit is incorrect permissions on ~/.ssh or authorized_keys. SSH is picky: the .ssh directory must be 700, and authorized_keys must be 600. If someone edited the file as root and didn't fix ownership, the SSH daemon ignores it.

Another common issue is SELinux on RHEL-based systems. Check the audit log:

sudo ausearch -m avc -ts recent

If you see denials related to SSH, restore the correct context:

sudo restorecon -Rv ~/.ssh

Finally, verify the key fingerprint matches. On your local machine:

ssh-keygen -lf ~/.ssh/id_ed25519.pub

On the server:

ssh-keygen -lf ~/.ssh/authorized_keys

If the fingerprints differ, you copied the wrong key.

Next steps after your first deploy

Your VPS is live, locked down, and serving traffic. From here, add automated backups (rsync to object storage, database dumps to a separate volume), set up SSL with Let's Encrypt (certbot --nginx), and configure monitoring (Netdata, Prometheus, or even a simple uptime checker).

Keep your system updated weekly with apt upgrade or schedule unattended-upgrades for security patches. Review your firewall rules whenever you add a service—don't leave ports open just because they were needed once.

A clean initial setup saves hours of troubleshooting later. The steps I've shown here are the foundation every production VPS needs before you worry about scaling, caching, or edge deployments.

FAQ

Can I skip creating a non-root user?

You can, but you shouldn't. Running as root means every command, script, and application has full system access. A single mistake or compromised service can destroy the entire server. Sudo gives you control when you need it and safety when you don't.

Should I disable IPv6 in SSH?

Only if your VPS doesn't have an IPv6 address. Most providers assign both IPv4 and IPv6. Disabling IPv6 in sshd_config (AddressFamily inet) can prevent bind errors on IPv4-only systems, but it's not necessary otherwise.

How do I change the SSH port if I'm already locked out?

Log in via your provider's web console (VNC, serial console, or recovery mode). Revert the port in /etc/ssh/sshd_config, restart SSH, and update your firewall rules before trying again.

Do I need fail2ban if I've disabled password authentication?

It's optional. Fail2ban blocks IPs after repeated failed login attempts, which matters for password auth. With key-based auth, attackers can't brute-force their way in, so fail2ban mostly reduces log noise. It won't hurt to install it, though.

What if my app doesn't start after a reboot?

Check that you ran pm2 startup and pm2 save (or the equivalent for your process manager). Without those commands, PM2 won't know to restart your app on boot. Verify with pm2 list and sudo systemctl status pm2-deploy.