π Deploy Golang API to VPS with Nginx & HTTPS
This guide documents the complete production deployment process of a Golang API to a VPS.
Prerequisites:
- A VPS with Ubuntu 22.04 (You can get one from SumoPod)
- A domain name (You can get one from Namecheap | Cloudflare | etc)
- A Golang API
We will:
- Install required packages
- Configure firewall (UFW)
- Clone project
- Configure environment variables
- Build and run binary
- Setup systemd service
- Configure Nginx reverse proxy
- Point custom domain
- Enable HTTPS (SSL)
- Verify auto renewal
π Final Architecture
Internet
β
HTTPS (443)
β
Nginx Reverse Proxy
β
http://localhost:6969
β
Go Binary (/opt/<appname>/<appname>)
β
systemd Service
Phase 1 β Install Requirements
# Connect to your vps
ssh root@YOUR_VPS_IP
# Update package list
sudo apt update && sudo apt upgrade -y
# Install Nginx, Git, Certbot, and Build Tools
sudo apt install -y git uvw curl nginx build-essential
Description:
- git: version control system
- uvw: (Uncomplicated Firewall) tool for managing firewall rules
- curl: tool for transferring data with URLs
- nginx: web server and reverse proxy
- build-essential: collection of packages required for building software from source code
Phase 2 - Create propper user
# Create a new user
sudo adduser vicktor
# Add the user to the sudo group
sudo usermod -aG sudo vicktor
# Switch to the new user
su - vicktor
Phase 3 β Configure Firewall (UFW)
# Allow SSH, Nginx Full
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
Enable UFW
sudo ufw enable
Check status
sudo ufw status
Output:
Status: active
To Action From
-- ------ ----
OpenSSH ALLOW Anywhere
Nginx Full ALLOW Anywhere
OpenSSH (v6) ALLOW Anywhere (v6)
Nginx Full (v6) ALLOW Anywhere (v6)
Phase 4 β Clone & Build Project
Move to /opt:
cd /opt
Clone the repository:
# Clone the repository
git clone <github_repo>/username/repo_name.git
# Navigate to the project directory
cd repo_name
Change ownership of the project directory:
sudo chown -R $USER:$USER /opt/repo_name
Configure environtment variables
cd /opt/repo_name
cp .env.example .env
Build the project
cd /opt/repo_name
go build -buildvcs=false -o <appname>
Verify
If successful, no output will appear.
Check:
ls -l <appname>
Output:
-rwxr-xr-x 1 <user> <user> <size> <date> <appname>
Test Application manually
./<appname>
Output:
<appname> is running on port <port>
Test locally your API health check endpoint:
curl http://localhost:<port>/health
Output:
{
"status": "ok"
}
If it’s works, press Ctrl + C to stop the application.
Phase 5 β Setup systemd service
Create service:
sudo nano /etc/systemd/system/<appname>.service
[Unit]
Description=<appname> API
After=network.target
[Service]
User=root
WorkingDirectory=/opt/<appname>
ExecStart=/opt/<appname>/<appname> http
Restart=always
Environment=APP_ENV=production
[Install]
WantedBy=multi-user.target
Reload systemd:
sudo systemctl daemon-reexec
sudo systemctl daemon-reload
Enable and start the service:
sudo systemctl enable <appname>
sudo systemctl start <appname>
Check status:
sudo systemctl status <appname>
You will see the output like this:
β <appname>.service - <appname> API
Loaded: loaded (/etc/systemd/system/<appname>.service; enabled; preset: enabled)
Active: active (running) since <date> <time>; <time> ago
Main PID: <pid> (<appname>)
Tasks: 1 (limit: <limit>)
Memory: <memory>MiB
CPU: <cpu>ms
CGroup: /system.slice/<appname>.service
ββ<pid> /opt/<appname>/<appname> http
Veryfy port listening:
ss -tulnp | grep 6969
Output:
tcp LISTEN 0 4096 *:6969 *:* users:(("<appname>",pid=<pid>,fd=<fd>))
Phase 6 β Pointing Domain & Configure Nginx Reverse Proxy
Pointing Domain
Create DNS A record:
Type: A
Name: api
Value: <YOUR_VPS_IP>
wait for DNS propagation (2-5 minutes).
verify DNS propagation on your vps:
ping api.<your_domain>
Output:
PING api.<your_domain> (<YOUR_VPS_IP>): 56 data bytes
64 bytes from <YOUR_VPS_IP>: icmp_seq=0 ttl=64 time=0.053 ms
64 bytes from <YOUR_VPS_IP>: icmp_seq=1 ttl=64 time=0.048 ms
...
Configure Nginx Reverse Proxy
sudo nano /etc/nginx/sites-available/<your_domain>
paste:
server {
listen 80;
server_name api.<your_domain>;
location / {
proxy_pass http://localhost:6969;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
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-For warded-Proto $scheme;
}
}
Enable the site:
sudo ln -s /etc/nginx/sites-available/<your_domain> /etc/nginx/sites-enabled/
Test Nginx configuration:
sudo nginx -t
Output:
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
Reload Nginx:
sudo systemctl reload nginx
Verify:
curl -I http://api.<your_domain>
Output:
HTTP/1.1 200 OK
Server: nginx
Date: <date>
Content-Type: application/json
Content-Length: 13
Connection: keep-alive
Phase 7 β Enable HTTPS (Letβs Encrypt)
Install certbot:
sudo apt install certbot python3-certbot-nginx
// Description
- certbot: tool for obtaining and renewing SSL certificates
- python3-certbot-nginx: Nginx plugin for certbot
Rrequest Certificate:
sudo certbot --nginx -d api.<your_domain>
Output:
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator Nginx, Installer Nginx
Requesting new certificate
Performing the following challenges:
http-01 challenge for api.<your_domain>
Waiting for verification...
Cleaning up challenges
...
Verify:
curl -I https://api.<your_domain>
Output:
HTTP/2 200 OK
server: nginx
date: <date>
content-type: application/json
content-length: 13
last-modified: <date>
etag: "<etag>"
strict-transport-security: max-age=31536000; includeSubDomains
Auto Renew Certificate
sudo certbot renew --dry-run
Final Result
Your production API is live at:
https://api.<your_domain>
Check your Health Endpdoint:
curl https://api.<your_domain>/health
Output:
{
"status": "ok"
}
π Conclusion
You now have:
- Golang API running on VPS
- Managed by systemd
- Reverse proxied via Nginx
- Custom domain configured
- HTTPS enabled
- Auto SSL renewal
- Production-ready deployment
π Next Steps
- Configure database
- Add monitoring and logging
- Implement rate limiting
- Add authentication and authorization
- Add caching
- Add rate limiting
- Add rate limiting
