Complete Guide

From Droplet to Live:
Your First Production Server

A complete, no-fluff guide to setting up a production-ready server from scratch. Real commands, real explanations, and real-world analogies — written for people who learn by doing.

By Love Chauhan | April 2026 | ~35 min read
Section 00

Why Build Your Own Server?

Because drag-and-drop hosting won't cut it forever.

If you've ever built a website, you've probably used services like Netlify or Vercel — drag your files, get a link, done. And that's great for simple static sites. But the moment you need a database, a backend API, email sending, cron jobs, or multiple websites on one machine — you need your own server.

This guide walks you through everything — from creating a blank server to having multiple live websites with SSL certificates, firewall protection, and automated security updates. No frameworks, no Docker, no Kubernetes. Just a clean Linux server that you actually understand.

Who is this for? This guide is for beginners who want to learn server management the right way. You don't need any prior Linux experience — every command is explained in plain English with real-world analogies.

What you'll have by the end:

  • A secured Ubuntu server on DigitalOcean
  • Nginx serving multiple websites
  • Free SSL certificates (auto-renewing)
  • Firewall with only necessary ports open
  • Automatic security updates
  • Fail2Ban blocking attackers
  • FileZilla connected for easy file management
Section 01

Create a Droplet

Your server starts with a few clicks.

A "Droplet" is DigitalOcean's name for a virtual server. Think of it as renting a computer in a data center somewhere — it runs 24/7, has its own IP address, and you control it entirely from your terminal.

Step 1 — Generate an SSH Key

Before creating the Droplet, you need an SSH key. This is like a digital fingerprint — it lets you log into your server without a password (and it's way more secure).

Open PowerShell on your Windows PC and run:

PowerShell — Your PC
ssh-keygen -t rsa -b 4096

Press Enter for all the questions (default location and no passphrase is fine for now). This creates two files:

  • id_rsa — Your private key (never share this with anyone)
  • id_rsa.pub — Your public key (this goes on the server)

To find your public key, run:

PowerShell — Your PC
cat ~/.ssh/id_rsa.pub

Copy the entire output — you'll need it in the next step.

Step 2 — Create the Droplet

Go to DigitalOcean and create a new Droplet with these settings:

  • OS: Ubuntu 24.04 LTS (Long Term Support — gets security updates until 2029)
  • Plan: Pick based on your needs (1GB RAM is fine for a few static sites)
  • Region: Choose the closest to your target audience
  • Authentication: Select SSH Key → paste your public key from Step 1
  • Hostname: Give it a meaningful name like "production-server"
Tip Always choose LTS versions of Ubuntu. LTS = Long Term Support, meaning it gets security updates for 5 years. Non-LTS versions lose support quickly and aren't recommended for production.

Once created, DigitalOcean gives you an IP address — something like 143.110.52.87. Save this. It's your server's home address on the internet.

Section 02

First Login via SSH

Time to enter your new server for the first time.

SSH (Secure Shell) is how you remotely control your server. It's like opening a terminal window directly on that faraway computer. Open PowerShell and run:

PowerShell — Your PC
ssh root@143.110.52.87

Replace 143.110.52.87 with your actual Droplet IP. If it asks "Are you sure you want to continue connecting?" — type yes and press Enter. This only happens the first time.

You should see something like:

Server Terminal
Welcome to Ubuntu 24.04 LTS
root@production-server:~#

That root@production-server:~# means you're in. You're now controlling your server. The root part means you're the admin — you have full control over everything.

Note Since you're logged in as root (the super admin), you don't need to type sudo before commands. Every command in this guide assumes you're root.
Section 03

Update & Upgrade

First thing you do on any new server — update everything.

Think of this like updating apps on your phone. Your server came with software pre-installed, but there might be newer versions available with bug fixes and security patches.

Server Terminal
# Step 1 — Check what updates are available
apt update

# Step 2 — Install all available updates
apt upgrade -y
  • apt update — Like checking "Are there any updates?" (just checking, not installing)
  • apt upgrade -y — Like clicking "Install all updates" (the -y means "yes to everything" so it doesn't keep asking for confirmation)
Important This only updates software packages within your current Ubuntu version. It will NOT upgrade Ubuntu 24.04 to 25.04 or any other version. That's a completely different command (do-release-upgrade) — and you should generally avoid it on production servers.

If during the upgrade you see a prompt asking about configuration files (like "What do you want to do about modified sshd_config?"), always choose "keep the local version currently installed". This preserves your existing settings.

Section 04

Reboot the Server

Some updates need a restart to take effect.

Just like your PC sometimes says "Restart to finish installing updates" — your server needs the same after major upgrades.

Server Terminal
reboot

Your terminal will disconnect — that's completely normal. The server is restarting. Wait about 30 seconds, then reconnect:

PowerShell — Your PC
ssh root@143.110.52.87
When to reboot vs restart a service reboot restarts the entire server — only needed after OS-level upgrades. systemctl restart nginx restarts only Nginx — use after config changes. You don't need to reboot the whole server every time you change something.
Section 05

Install Nginx

The software that actually serves your websites to visitors.

Nginx (pronounced "Engine-X") is a web server. When someone types your domain in their browser, Nginx is the program that receives that request and sends back your HTML, CSS, and JS files. Think of it as a receptionist — it listens at the door (ports 80 and 443) and hands visitors the right files.

Server Terminal
# Install Nginx
apt install nginx -y

# Start it up
systemctl start nginx

# Make sure it starts automatically on reboot
systemctl enable nginx

# Check it's running
systemctl status nginx

You should see "active (running)" in green. At this point, if you visit your server's IP in a browser (http://143.110.52.87), you'll see the default "Welcome to Nginx" page. That means it's working!

Section 06

Understanding Ports

Before we set up the firewall, let's understand what we're protecting.

Every server has 65,535 ports. Think of your server as a building with 65,535 doors. Each door serves a specific purpose — some let visitors in, some let you manage the building, and most should stay locked.

Port Service What It Does
22 SSH Your remote login (PowerShell, FileZilla)
80 HTTP Website without SSL (the "not secure" version)
443 HTTPS Website with SSL (the secure lock icon version)
21 FTP Old-school file transfer (we don't use this)
25 SMTP Sending emails
3306 MySQL Database connections
5432 PostgreSQL Another type of database

How Hackers Attack Through Ports

Port 22 (SSH) — If this door is open and you use a password, hackers run brute-force attacks — trying thousands of passwords per second like "admin123", "password", "root123" until one works. That's why we use SSH keys instead — it's like replacing a regular lock with a fingerprint scanner.

Port 80/443 (Websites) — Hackers try SQL injection (typing code in your login form to trick your database), or DDoS attacks (sending millions of fake visitors to crash your site — like a crowd blocking a shop entrance so real customers can't enter).

Port 3306 (Database) — If this door is open to the internet, anyone can try to connect to your database directly and steal your data. That's why databases should only be accessible locally.

Port 25 (Email) — Hackers can hijack your server to send spam emails. Your server gets blacklisted and your real emails stop working.

The Rule Closed door = Hackers can't even see it exists. Open door + weak security = Easy target. Open door + strong security = Safe. That's why we only open what we need.
Section 07

Firewall Setup (UFW)

Lock all 65,535 doors, then only open the ones you need.

UFW stands for Uncomplicated Firewall — and it really is uncomplicated. Right now all 65,535 ports are wide open. We're going to lock them all and only open three: 22 (SSH), 80 (HTTP), and 443 (HTTPS).

Critical Run these commands in this exact order. If you enable the firewall before allowing SSH, you'll lock yourself out of your own server.
Server Terminal
# Step 1 — Allow SSH first (so you don't get locked out!)
ufw allow OpenSSH

# Step 2 — Allow web traffic (HTTP + HTTPS)
ufw allow 'Nginx Full'

# Step 3 — Turn the firewall ON
ufw enable

It will ask "Command may disrupt existing SSH connections. Proceed?" — type y and press Enter. Don't worry, you already allowed SSH so your connection stays alive.

Verify everything:

Server Terminal
ufw status

You should see OpenSSH and Nginx Full both showing ALLOW. Everything else is blocked by default.

Future Ports Later, when you add databases or backend apps, you open more ports as needed: ufw allow 3306 for MySQL, ufw allow 3000 for a Node.js app, etc. Only open what you actually use.
Section 08

Security Hardening

Three extra layers of protection that run on autopilot.

8.1 — Verify Password Login is Disabled

Since you chose SSH key during Droplet creation, password login should already be disabled. But DigitalOcean has a sneaky config file called cloud-init that can override your main SSH settings. Let's verify both:

Server Terminal
# Check the main SSH config
grep PasswordAuthentication /etc/ssh/sshd_config

# Check the cloud-init override (the sneaky one!)
grep PasswordAuthentication /etc/ssh/sshd_config.d/50-cloud-init.conf

You want both to show PasswordAuthentication no. If the main config shows #PasswordAuthentication yes (with a # at the start), that's fine — the # means it's commented out (disabled). The cloud-init file is the one that actually matters.

8.2 — Enable Automatic Security Updates

This makes your server automatically install critical security patches in the background — like Windows auto-update but only for security fixes.

Server Terminal
# Install the auto-update tool
apt install unattended-upgrades -y

# Configure it — select "Yes" when asked
dpkg-reconfigure --priority=low unattended-upgrades

Breaking down that second command: dpkg-reconfigure means "open the settings of an installed tool." --priority=low means "show me all the options." unattended-upgrades is the tool name — "unattended" means "without you being there" and "upgrades" means "updates."

8.3 — Install Fail2Ban

Fail2Ban watches your server's logs and if someone fails to login multiple times (like 5 wrong attempts), it automatically bans their IP address. Think of it as a bouncer who kicks out troublemakers.

Server Terminal
# Install Fail2Ban
apt install fail2ban -y

# Create a local config (so updates don't overwrite your settings)
cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local

# Start it and enable on boot
systemctl start fail2ban
systemctl enable fail2ban

# Verify it's running
systemctl status fail2ban

Default settings are solid — it bans anyone who fails 5 login attempts for 10 minutes. You'll see "active (running)" if everything's good.

8.4 — Block Direct IP Access

Right now, if someone types your server's IP address in a browser, they'll see one of your websites (whichever loads first alphabetically). That's not ideal — it tells hackers "hey, this is an active Nginx server." Let's make the IP show nothing.

Create a file called 00-block-ip in /etc/nginx/sites-available/ with this content:

Nginx Config — /etc/nginx/sites-available/00-block-ip
# Block direct IP access — drop connection silently
server {
    listen 80 default_server;
    server_name _;
    return 444;
}

The underscore _ means "no real domain" — it catches all requests that don't match any domain. 444 is Nginx's special code that silently closes the connection. The file is named 00-block-ip so it loads first alphabetically.

Server Terminal
# Enable it
ln -s /etc/nginx/sites-available/00-block-ip /etc/nginx/sites-enabled/

# Test and reload
nginx -t
systemctl reload nginx

Now visiting http://143.110.52.87 directly shows nothing. Connection drops silently.

Section 09

Connect FileZilla (SFTP)

A visual way to manage your server's files — drag and drop instead of commands.

FileZilla lets you browse your server's files just like File Explorer on Windows. You can drag-and-drop files between your PC and server instead of typing commands.

Open FileZilla → FileSite ManagerNew Site and fill in:

  • Protocol: SFTP - SSH File Transfer Protocol
  • Host: 143.110.52.87 (your server IP)
  • Port: 22
  • Logon Type: Key file
  • User: root
  • Key file: Browse to C:\Users\YOUR_USERNAME\.ssh\id_rsa
Tip If FileZilla doesn't show your id_rsa file, change the file filter at the bottom from "PPK files" to "All files". FileZilla will ask to convert it to .ppk format — click Yes and save it.

Click Connect. You should see your server's files on the right side of FileZilla. Navigate to /var/www/ — this is where all your website files will live.

Section 10

Upload Website Files

Put your site files where Nginx can find them.

The standard location for website files on a Linux server is /var/www/. Each website gets its own folder:

Server File Structure
/var/www/
├── mysite.com/
│   └── index.html
├── blog.mysite.com/
│   └── index.html
├── app.mysite.com/
│   └── index.html
└── html/              ← default Nginx page (we disabled this)

In FileZilla, navigate to /var/www/ on the right panel. Then on the left panel (your PC), find your website files. Simply drag and drop your website folder from left to right.

You can also create folders directly in FileZilla — right-click on the right panel → Create directory → name it after your domain.

File Structure Matters Make sure your index.html is at the root of your site folder — not buried inside a subfolder. If your files are inside a html/ subfolder, you'll need to account for that in your Nginx config later.
Section 11

Nginx Config Files

Telling Nginx which domain goes to which folder.

Nginx needs to know: "When someone visits mysite.com, which folder should I serve files from?" That's what config files do. They live in two folders:

  • /etc/nginx/sites-available/ — All your config files (like a filing cabinet)
  • /etc/nginx/sites-enabled/ — Only the active ones (shortcuts pointing to the cabinet)

Create a Config File

In FileZilla, navigate to /etc/nginx/sites-available/. Right-click → Create new file → name it after your domain (e.g., mysite.com). Then right-click → View/Edit and paste:

Nginx Config — /etc/nginx/sites-available/mysite.com
# Config for mysite.com
server {
    # Listen on port 80 (HTTP) — Certbot will add SSL later
    listen 80;

    # Domain names this site responds to
    server_name mysite.com www.mysite.com;

    # Where the site files are stored
    root /var/www/mysite.com;

    # Default file to load
    index index.html;

    # How to handle requests
    location / {
        # Try the URL as a file, then folder, then show 404
        try_files $uri $uri/ =404;
    }
}

Save and close. Each line explained:

  • listen 80 — Listen on port 80 (HTTP, no SSL yet — Certbot adds that later)
  • server_name — The domain(s) that trigger this config
  • root — The folder path where your website files live
  • index — The default file to serve when someone visits the root URL
  • try_files — If someone visits a URL, check if a file exists at that path, then check if it's a folder, otherwise show a 404 error

Enable the Site

Creating the config doesn't activate it. You need to create a shortcut (symlink) from sites-available to sites-enabled:

Server Terminal
# Create the shortcut
ln -s /etc/nginx/sites-available/mysite.com /etc/nginx/sites-enabled/

# Test if the config file has no errors
nginx -t

# If test passes, reload Nginx
systemctl reload nginx

The nginx -t command is your safety net — it checks for syntax errors before applying changes. Always run it before reloading. If it shows "syntax is ok" and "test is successful" — you're good.

Multiple Sites Repeat this process for each website. Each site gets its own config file in sites-available and its own symlink in sites-enabled. One server can host as many sites as you want.

Disable the Default Site

Nginx comes with a default config that shows the "Welcome to Nginx" page. Remove it:

Server Terminal
# Remove the shortcut (original file stays safe in sites-available)
rm /etc/nginx/sites-enabled/default

# Test and reload
nginx -t
systemctl reload nginx

This only removes the shortcut, not the actual config file. If you ever need it back, just create the symlink again. With the default disabled, only your actual domains will work — no more "Welcome to Nginx" page on the bare IP.

Section 12

Point Your Domain (DNS)

Connecting your domain name to your server's IP address.

Right now, your website lives at http://143.110.52.87 — but nobody's going to remember that. DNS (Domain Name System) is like the internet's phone book — it translates mysite.com to 143.110.52.87.

Go to your domain registrar (wherever you bought your domain — Namecheap, GoDaddy, Cloudflare, etc.) and add A records:

Type Host Value
A @ 143.110.52.87
A www 143.110.52.87

The @ symbol means the root domain (mysite.com), and www handles www.mysite.com.

For subdomains (like blog.mysite.com or app.mysite.com), add more A records:

Type Host Value
A blog 143.110.52.87
A app 143.110.52.87

DNS changes can take anywhere from a few minutes to a few hours to propagate. You can verify if DNS has updated using:

Server Terminal
# Check if your domain points to the right IP
dig mysite.com +short
dig www.mysite.com +short

Both should return your server's IP. Only proceed to SSL once DNS is fully propagated — Certbot needs to verify domain ownership.

Section 13

Install SSL (HTTPS)

Free HTTPS with Let's Encrypt — because "Not Secure" warnings kill trust.

SSL makes your site use https:// instead of http:// — that lock icon in the browser. Without it, browsers show a scary "Not Secure" warning to your visitors. Certbot makes this completely free using Let's Encrypt certificates.

Install Certbot

Server Terminal
apt install certbot python3-certbot-nginx -y

Get a Certificate

Run Certbot for each domain:

Server Terminal
# For your main site (includes www)
certbot --nginx -d mysite.com -d www.mysite.com

# For a subdomain
certbot --nginx -d blog.mysite.com

What each flag does:

  • --nginx — "I'm using Nginx, handle everything automatically" (Certbot edits your Nginx config to add SSL)
  • -d — Each -d adds a domain to the certificate

The first time Certbot runs, it asks for your email address (for expiry reminders) and to agree to terms. After that, it handles everything automatically — generates the certificate, updates your Nginx config, and reloads Nginx.

Verify Your Certificates

Server Terminal
# See all your active certificates
certbot certificates

Test Auto-Renewal

Let's Encrypt certificates expire every 90 days, but Certbot automatically renews them. Test that the auto-renewal works:

Server Terminal
# Dry run = pretend to renew (doesn't actually change anything)
certbot renew --dry-run

If you see "Congratulations, all simulated renewals succeeded" — your certificates will auto-renew forever without you lifting a finger.

Note Certbot with the --nginx flag automatically reloads Nginx after getting the certificate. No need to restart manually.
Section 14

Troubleshooting

Common issues you'll run into (and how to fix them).

Nginx Config Test Fails

If nginx -t throws an error like "open() failed — No such file or directory", it usually means you have a symlink in sites-enabled pointing to a config file that doesn't exist yet.

Server Terminal
# List all symlinks in sites-enabled
ls -la /etc/nginx/sites-enabled/

# Remove the broken one
rm /etc/nginx/sites-enabled/broken-config-name

# Test again
nginx -t

403 Forbidden Error

This means Nginx found the folder but can't serve files from it. Two common causes:

Cause 1: No index.html in the root folder. Your files might be inside a subfolder. Check:

Server Terminal
ls -la /var/www/mysite.com/

If you see a html/ subfolder containing your files, either move the files up or update your Nginx config's root line to /var/www/mysite.com/html.

Cause 2: Permission issues. Fix with:

Server Terminal
chmod -R 755 /var/www/mysite.com/

Certbot Fails — Domain Not Pointing

If Certbot gives "unauthorized" or mentions the wrong IP address, your DNS hasn't fully propagated yet. Verify with dig yourdomain.com +short and wait until it shows your server's IP.

Cloud-Init SSH Override

You edited /etc/ssh/sshd_config to disable password login, but it's still enabled? Check the cloud-init override file — it takes priority over the main config:

Server Terminal
cat /etc/ssh/sshd_config.d/50-cloud-init.conf

If it shows PasswordAuthentication yes, edit it and change to no, then restart SSH:

Server Terminal
systemctl restart sshd
Section 15

Final Checklist

Make sure everything is locked down before you go live.

  • SSH key login working — no password
  • Password authentication disabled (check cloud-init too!)
  • System packages updated and upgraded
  • Nginx installed and running
  • Firewall enabled — only ports 22, 80, 443 open
  • Automatic security updates enabled
  • Fail2Ban active and running
  • Direct IP access blocked (returns nothing)
  • Website files uploaded to /var/www/
  • Nginx config files created and enabled
  • Default Nginx site disabled
  • DNS A records pointing to server IP
  • SSL certificates installed for all domains
  • SSL auto-renewal tested with dry-run
  • All sites loading with https:// and lock icon
You did it! If everything above is checked, congratulations — you have a production-ready server that's secure, automated, and ready to serve your websites to the world. The next time someone visits your domain, it passes through DNS → hits your firewall → reaches Nginx → serves your files over HTTPS. All set up by you, from scratch.