I Thought Adding a Subdomain Was Easy—Then My Server Cried for Help

I Thought Adding a Subdomain Was Easy—Then My Server Cried for Help
Photo by Lee Campbell / Unsplash

Two days ago I almost hanged my server.

I wanted to add a new subdomain and SSL and i thought it was nothing, and them Boom, all my nginx configs went out of sync.

But, my base domain was unreachable and my VPN was throwing errors. it forced me to understand how to do things proplerly–especially when you're running multiple services on the same server.

If you have same situation, maybe you've got a V2Ray VPN running and a Reflex web app, and you want to serve each on their own clean subdomain like vpn.domain.com and app.domain.com, here's how I got everything working.

P.S. If you want to learn more about the difference between subdomains and subdirectories—and which one is better for SEO—check out my detailed article here:

Subdomain vs Subdirectory: What’s the Difference and Which One Is Better for SEO?
I have seen websites with addresses like blog.domain.com and asked myself how that’s different from something like domain.com/blog? These two url — Subdomains & Subdirectories, might seem similar , but they can have a big effect on your SEO strategy, site structure and user experience. In This Article

What I Wanted

  • V2Ray VPN accessible via vpn.domain.com
  • Reflex web app accessible via app.domain.com
  • Proper SSL (HTTPS) using Let’s Encrypt
  • No www (i.e., no www.app.domain.com)
  • Clean nginx config without breaking my existing domain

The Problem I Faced

I tried to just replace some nginx lines and change the domain. It kind of worked — until it didn’t. The biggest problems were:

  • Using one config file for everything.
  • Forgetting to remove the default nginx config, which overrides everything.
  • Adding SSL before the domain was actually reachable (so Certbot failed).
  • Not creating dedicated nginx config files for each service.
  • Confusion between base domain domain.com and subdomains like app.domain.com.

But I fixed everything , So here’s how step by step.


The Working Setup (All Code in One Place)

# First, set up DNS A records for your subdomains
# Go to your domain registrar and add:
# app.domain.com → your.server.ip
# blog.domain.com → your.server.ip

# Make sure your server is reachable - change it to your own domain
ping app.domain.com
ping blog.domain.com

# On your server: remove default nginx config
sudo rm /etc/nginx/sites-enabled/default
sudo rm /etc/nginx/sites-available/default

# Create nginx config for VPN (vpn.domain.com)
sudo nano /etc/nginx/sites-available/v2ray

Then run:

# Redirect HTTP to HTTPS
server {
    listen 80;
    server_name vpn.domain.com;
    return 301 https://$host$request_uri;
}

# VPN HTTPS server block
server {
    listen 443 ssl;
    server_name vpn.domain.com;

    ssl_certificate /etc/letsencrypt/live/vpn.domain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/vpn.domain.com/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

    location / {
        proxy_pass http://127.0.0.1:10000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
    }
}
# Create nginx config for Reflex app (app.domain.com)
sudo nano /etc/nginx/sites-available/reflex

Then run:

# Redirect HTTP to HTTPS
server {
    listen 80;
    server_name app.domain.com;
    return 301 https://$host$request_uri;
}

# Reflex Web App HTTPS server block
server {
    listen 443 ssl;
    server_name app.domain.com;

    ssl_certificate /etc/letsencrypt/live/app.domain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/app.domain.com/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

After that:

# Enable both sites
sudo ln -s /etc/nginx/sites-available/v2ray /etc/nginx/sites-enabled/
sudo ln -s /etc/nginx/sites-available/reflex /etc/nginx/sites-enabled/

# Check nginx config
sudo nginx -t

# Reload nginx
sudo systemctl reload nginx

# Install Certbot and add SSL
sudo apt install certbot python3-certbot-nginx -y

# Add SSL to both subdomains
sudo certbot --nginx -d vpn.domain.com
sudo certbot --nginx -d app.domain.com

# Enable automatic certificate renewal
sudo systemctl enable certbot.timer

What I've Learned

  • You don’t need www. for subdomains like vpn. — just point vpn.domain.com to your IP in DNS and nginx will handle the rest.
  • Always create separate config files per domain or app. It avoids conflict and makes it easy to debug.
  • Remove the default nginx config if you’re not using it. It can override everything silently.
  • Run Certbot only after your DNS is active and nginx is serving the domain.
  • Use ports wisely — I ran my VPN on 10000 and Reflex app on 3000.
Related Reading

Final Thoughts

It’s easy to get confused when you’re managing multiple domains on a single VPS — but once you split things cleanly, it becomes very easier to scale.

Now my setup works without problem. VPN traffic on vpn.domain.com, my app on app.domain.com, and no weird nginx bugs anymore.

And I learned a valuable lesson: don’t rush DNS + SSL configs. Break things once, then fix it like a pro.
You can fuel my next data science deep dive by buying me a coffee ☕!