# Initial VPS Configuration Guide: Building a solid foundation for your web application

Using a Virtual Private Server (VPS) offers a powerful and budget-friendly way to gain full control over your hosting environment. Whether you're making your first deployment or migrating from shared hosting to something more robust, a proper initial setup makes all the difference. This guide will walk you through getting your new VPS ready—secure, optimized, and prepared for deploying your applications, especially if you're aiming for a Dockerized setup. If you aim to deploy your web app without using Docker, check out this guideline [Host .NET 8 Asp Net Core and Blazor on Linux VPS](https://kristiadhy.hashnode.dev/guide-to-hosting-net-8-aspnet-core-and-blazor-web-assembly-on-a-linux-vps)

You can skip some steps if you want to and choose what's best for you.

### Rent Your Virtual Private Server (VPS).

The first step is to choose a reputable hosting provider. Popular options include DigitalOcean, Vultr, and Hetzner. When selecting a plan, consider your application's resource requirements:

* **CPU:** More cores generally translate to better performance, crucial if you anticipate significant traffic or compute-intensive tasks.
    
* **RAM:** Ample memory ensures your applications run smoothly, preventing slowdowns and crashes under load.
    
* **Storage:** Opt for **SSD** (Solid State Drive) storage for significantly faster read/write speeds, which benefits database operations and application loading times. Ensure you have enough capacity for your application files, databases, and any future growth.
    
* **Bandwidth:** Important if your application handles a large volume of data transfers, such as file downloads, streaming media, or high traffic.
    

For a small application or testing environment, a basic plan might suffice. However, for larger or production-ready applications, investing in a more powerful plan from the start can save you headaches down the line and ensure a scalable foundation.

### Secure Your Root Account.

Upon initial login to your new VPS (usually as the `root` user), the very first thing you should do is change the default password. This is a critical security measure as default passwords are often weak or publicly known.

To change it, simply type:

```csharp
passwd
```

You'll be prompted to enter your new password twice. Choose a strong, unique password that combines uppercase and lowercase letters, numbers, and symbols.

### Create a Non-Root User

Working directly as the `root` user is a significant security risk. The `root` user has absolute control over the system, and a single misplaced command can lead to irreversible damage. To mitigate this, it's a best practice to create a new, unprivileged user for your day-to-day operations and grant it `sudo` privileges for administrative tasks. This aligns with the principle of "least privilege" and contributes to a more secure and maintainable system.

To create a new user (replace `myusername` with your desired username):

```csharp
adduser myusername
```

Follow the prompts to set a strong password for this new user and fill in any additional information (you can usually press Enter to skip these if not needed).

Next, grant this new user `sudo` permissions, allowing them to execute commands with `root` privileges when necessary, by prefixing the command with `sudo`.

```csharp
usermod -aG sudo myusername
```

Now, you can log out of the `root` user and log in as `myusername`. From this point forward, you'll use `sudo` for any administrative operations.

### Enhance Security with SSH Keys.

After changing your password, the next level of security involves disabling password-based SSH logins entirely and exclusively using SSH keys. SSH keys provide a much stronger authentication mechanism, making brute-force password attacks virtually impossible. This is a fundamental step in securing your server.

**Step-by-Step SSH Key Setup:**

1. **Generate an SSH key on your local machine:** Open your local terminal and generate a new SSH key pair. `ed25519` is the modern and recommended algorithm due to its strong security and smaller key sizes, but `rsa` is also an option.
    
    ```csharp
    ssh-keygen -t ed25519
    ```
    
    Press `Enter` when prompted for a file to save the key (it will default to `~/.ssh/id_ed25519`) and when asked for a passphrase. While a passphrase adds an extra layer of security, it means you'll need to enter it every time you use the key. For convenience, many users opt not to set one for their primary key, but consider it for higher-security scenarios.
    
2. **Copy the public key to your server:** This command securely copies your public key (`id_`[`ed25519.pub`](http://ed25519.pub)) to the `authorized_keys` file on your VPS, allowing you to log in with your key. Replace `myusername` and `your-server-ip` with your actual details.
    
    ```csharp
    ssh-copy-id myusername@your-server-ip
    ```
    
    You will be asked for your `myusername`'s password one last time.
    
3. **Test your SSH key login:** Before proceeding, try logging in to your VPS using your new user and SSH key to ensure it works correctly.
    
    ```csharp
    ssh myusername@your-server-ip
    ```
    
    If successful, you should be logged in without being prompted for a password.
    
4. **Disable password login on the server:** Once you've confirmed SSH key login works, edit the SSH daemon configuration file to disallow password authentication. This significantly hardens your server against unauthorized access.
    
    ```csharp
    sudo nano /etc/ssh/sshd_config
    ```
    
    Find the line `PasswordAuthentication yes` and change it to:
    
    ```csharp
    PasswordAuthentication no
    ```
    
    You might also want to ensure `PermitRootLogin no` is set to prevent direct root logins via SSH, forcing you to use your non-root user and then `sudo` if needed.
    
    Save and exit nano (Ctrl+O, Enter, Ctrl+X).
    
5. **Restart the SSH service:** Apply the changes by restarting the SSH service.
    
    ```csharp
    sudo systemctl restart ssh
    ```
    
    Now, your server is much more secure, relying solely on cryptographic SSH keys for authentication.
    

### Install and Configure UFW (Uncomplicated Firewall).

A firewall is your server's first line of defense, controlling what network traffic is allowed in and out. UFW (Uncomplicated Firewall) is a user-friendly front-end for `iptables`, making it easier to manage firewall rules. It's critical to configure your firewall before deploying any applications.

<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text"><strong>Crucial Warning:</strong> Be extremely careful to allow SSH access <strong>before</strong> enabling UFW, or you risk locking yourself out of your VPS.</div>
</div>

Here's a safe baseline configuration, ideal for web applications:

```csharp
sudo ufw default deny incoming       # Deny all incoming connections by default
sudo ufw default allow outgoing      # Allow all outgoing connections by default
sudo ufw allow OpenSSH               # Allow SSH connections (port 22)
sudo ufw allow 80/tcp                # Allow HTTP traffic (port 80)
sudo ufw allow 443/tcp               # Allow HTTPS traffic (port 443)
sudo ufw enable                      # Enable the firewall
```

You'll be asked to confirm enabling the firewall; type `y` and press Enter.

To verify your firewall's status and rules:

```csharp
sudo ufw status verbose
```

This setup allows essential services while blocking all other unsolicited incoming connections, significantly reducing your server's attack surface.

### Set the Correct Timezone and Locale.

Proper timezone and locale settings are essential for accurate logging, scheduling tasks, and ensuring your applications behave as expected across different geographical locations.

**Timezone Configuration:**

Set your server's timezone. For example, if you are in Jakarta, Indonesia, you'd set it to `Asia/Jakarta`.

```csharp
sudo timedatectl set-timezone Asia/Jakarta
```

Verify the change:

```csharp
timedatectl
```

This command will display current system time, RTC time, and your configured timezone.

**Locale Configuration:**

The locale determines the language, character encoding, and cultural conventions for your system. For most English-speaking systems, `en_US.UTF-8` is a common and robust choice.

Check your current locale:

```csharp
locale
```

If you need to set a new one:

```csharp
sudo update-locale LANG=en_US.UTF-8
```

You can list all available locales with:

```csharp
locale -a
```

After changing the locale, it's best to log out and log back in to ensure the changes are fully applied to your session.

### Enable SSH Rate Limiting with Fail2ban.

Fail2ban is a powerful intrusion prevention framework that scans log files (like those for SSH, web servers, etc.) for repeated failed login attempts and automatically bans the offending IP addresses for a specified period. This is an excellent defense against brute-force attacks.

**Installation:**

```csharp
sudo apt update
sudo apt install fail2ban -y
```

**Basic Configuration:**

1. Copy the default configuration file to a local override. This is a best practice, as it ensures your custom settings won't be overwritten during future `fail2ban` package updates.
    
    ```csharp
    sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
    ```
    
2. Edit the local configuration file:
    
    ```csharp
    sudo nano /etc/fail2ban/jail.local
    ```
    
    Look for the `[sshd]` section. Ensure it is enabled and review the `maxretry` and `bantime` settings:
    
    Ini, TOML
    
    ```csharp
    [sshd]
    enabled = true
    port = ssh
    filter = sshd
    logpath = %(sshd_log)s
    maxretry = 5   ; How many failed attempts allowed before banning
    bantime = 1h   ; How long to block the IP (e.g., 1h for 1 hour, 1d for 1 day)
    ```
    
    The `port` setting typically defaults to `ssh` (which resolves to port 22). If you've changed your SSH port, update this accordingly. `maxretry` defines the number of failed attempts before a ban, and `bantime` specifies the duration of the ban.
    
    Save and exit nano.
    
3. **Restart and Enable Fail2ban:**
    
    ```csharp
    sudo systemctl restart fail2ban
    sudo systemctl enable fail2ban # Ensure it starts on boot
    ```
    
4. **Check Status:**
    
    ```csharp
    sudo fail2ban-client status
    sudo fail2ban-client status sshd
    ```
    
    These commands will show you the active jails and any currently banned IPs for the SSH service. Fail2ban will now diligently monitor `/var/log/auth.log` (or your configured log path) and automatically ban repeat SSH failures, significantly enhancing your server's security posture.
    

### Install Malware/Rootkit Scanners.

While your primary defense should be proactive security measures, having tools to detect compromises is also vital. `rkhunter` (Rootkit Hunter) is a popular utility for scanning for rootkits, backdoors, and other potential vulnerabilities.

```csharp
sudo apt install rkhunter -y && \
sudo rkhunter --update && \
sudo rkhunter --propupd && \
sudo rkhunter --check
```

* `--update`: Updates the data files used by `rkhunter`.
    
* `--propupd`: Updates the file properties database. You should run this after a fresh install or major system updates to prevent false positives.
    
* `--check`: Performs the actual scan.
    

You should re-run `sudo rkhunter --check` regularly as part of your server maintenance routine to check for new issues.

### Uninstall Default Applications (If Unnecessary).

Many VPS providers pre-install common application stacks like LAMP (Linux, Apache, MySQL, PHP) to get users up and running quickly. However, if you plan to deploy your applications using Docker, these pre-installed components are often redundant and can introduce conflicts or unnecessary attack surfaces. Removing them provides a cleaner environment and ensures Docker has full control over application orchestration.

Here's how to remove common default software on an Ubuntu-based VPS:

```csharp
# Stop and Remove Apache
sudo systemctl stop apache2
sudo apt remove --purge apache2 apache2-utils apache2-bin apache2.2-common

# Stop and Remove MySQL (if installed directly, not in Docker)
sudo systemctl stop mysql
sudo apt remove --purge mysql-server mysql-client mysql-common

# Stop and Remove MariaDB (if installed directly, not in Docker)
sudo systemctl stop mariadb
sudo apt remove --purge mariadb-server mariadb-client

# Remove PHP and its modules (if installed directly)
sudo apt remove --purge php*

# Clean up any residual packages and orphaned dependencies
sudo apt autoremove
sudo apt autoclean
```

**How to check what's installed:**

If you're unsure what services are running or installed, you can use `dpkg -l` to list packages and `grep` to filter for specific ones:

```csharp
dpkg -l | grep apache
dpkg -l | grep mysql
dpkg -l | grep php
dpkg -l | grep mariadb
```

Remove anything that's not explicitly needed for your Dockerized environment. This contributes to a "clean architecture" for your server itself, reducing clutter and potential vulnerabilities.

### Set Up Your Hostname.

When you first provision your VPS, it's often assigned a generic hostname. It's good practice to map this properly within your system for easier identification and to ensure services that rely on hostname resolution function correctly.

1. **Check your current hostname:**
    
    ```csharp
    hostname
    ```
    
2. **Edit your** `/etc/hosts` file: This file maps hostnames to IP addresses locally on your server.
    
    ```csharp
    sudo nano /etc/hosts
    ```
    
    Look for a line that might resemble `127.0.1.1 oldhostname` and update it to reflect your VPS provider's assigned hostname, or a custom one if you prefer. For example:
    
    ```csharp
    127.0.1.1   your-vps-hostname
    ```
    
    Also, ensure you have the standard [localhost](http://localhost) entry:
    
    ```csharp
    127.0.0.1   localhost
    ```
    
    So, a typical `/etc/hosts` file will look something like this:
    
    ```csharp
    127.0.0.1   localhost
    127.0.1.1   your-vps-hostname
    # The following lines are desirable for IPv6 capable hosts
    ::1     localhost ip6-localhost ip6-loopback
    ff02::1 ip6-allnodes
    ff02::2 ip6-allrouters
    ```
    
    <div data-node-type="callout">
    <div data-node-type="callout-emoji">💡</div>
    <div data-node-type="callout-text"><strong>Important Note:</strong> This <code>/etc/hosts</code> mapping only affects local name resolution within your VPS. External clients will still need to use your domain name (with proper DNS records configured at your domain registrar) to reach your server.</div>
    </div>
    
    Save and exit nano (Ctrl+O, Enter, Ctrl+X).
    

### Install NGINX (Reverse Proxy for Dockerized Apps).

NGINX is an incredibly versatile and high-performance web server that excels as a reverse proxy. For modern, Docker-based application deployments, NGINX is almost indispensable.

**Why use NGINX?**

* **Reverse Proxy:** It acts as a single entry point for all incoming web traffic, routing requests to the appropriate Docker containers based on rules you define. This decouples your application containers from direct public exposure, enhancing security.
    
* **Load Balancing:** NGINX can distribute incoming traffic across multiple instances of your application (e.g., multiple Docker containers), improving scalability and fault tolerance.
    
* **Static File Serving:** It's highly efficient at serving static assets (HTML, CSS, JavaScript, images), offloading this task from your application containers.
    
* **SSL Termination:** NGINX can handle SSL/TLS encryption and decryption, allowing your application containers to run on plain HTTP internally, simplifying their configuration.
    
* **Security:** By sitting in front of your application, NGINX can filter malicious requests and provide an additional layer of defense.
    

In a Dockerized setup, NGINX often serves as the public-facing component, directing requests to your backend services running in containers. This aligns perfectly with a "clean architecture" by clearly separating concerns.

**Installation:**

```csharp
sudo apt update
sudo apt install nginx
```

After installation, verify that NGINX is running:

```csharp
sudo systemctl status nginx
```

You should see output indicating that the service is `active (running)`.

Enable NGINX to start automatically on boot:

```csharp
sudo systemctl enable nginx
```

To test if NGINX is working, open your VPS's IP address in a web browser. You should see the default NGINX welcome page, confirming that your web server is operational.
