DEV Community

Cover image for Deploying a Simple Go API with Supervisor and Nginx
Daniel Pepuho
Daniel Pepuho

Posted on • Originally published at danielcristho.site

14 1

Deploying a Simple Go API with Supervisor and Nginx

Intro

Hi! In this post, I'll show you how to deploy a simple Go API using Supervisor to manage the process and Nginx as a web server to serve it.

Before we dive into the deployment steps, let's briefly discuss why we're using Supervisor and Nginx.

  • Supervisor is a process control system that helps manage and monitor applications running in the background. It ensures that your Go API stays up and automatically restarts it if it crashes. See the full documentation

  • Nginx is a high-performance web server that can also function as a reverse proxy, making it ideal for serving our Go API to the internet. See the full documentation

🤔 Why Choose Supervisor Over Other Options?

You might wonder why we use Supervisor instead of alternatives like Systemd, PM2, or containerized solutions like Docker. Here’s a quick comparison:

Tools Pros Cons
Supervisor Simple setup, great for managing multiple processes, easy log management Requires manual config
Systemd Native to Linux, faster startup More complex setup, harder to debug
PM2 Built for Node.js, supports process monitoring Not ideal for Go applications
Docker Isolated environment, easy deployment, scalable More setup overhead, requires container knowledge

When Should You Use Supervisor?

Use Supervisor when you want a simple, non-containerized way to manage a Go service, with features like auto-restart and log management, without dealing with systemd’s complexity or Docker’s extra overhead.

Setup and Run a Simple Go API

Requirements

Before starting, make sure you have the following installed on your system:

  1. Go

    $ go version
    
    go version go1.24.0 linux/amd64
    

    If not installed, download it from the official site.

  2. Supervisor

  • Ubuntu/Debian

    $ sudo apt update
    $ sudo apt install supervisor -y
    
  • CentOS/RHEL

    $ sudo yum install supervisor -y
    
  • Homebrew (macOS)

    $ brew install supervisor
    

    After installation, check if Supervisor is running:

    $ sudo systemctl status supervisor
    

    If it’s not running, start and enable it:

    $ sudo systemctl start supervisor
    $ sudo systemctl enable supervisor
    
  1. Nginx
  • Ubuntu/Debian

    $ sudo apt install nginx -y
    
  • CentOS/RHEL

    $ sudo yum install nginx -y
    
  • Homebrew (macOS)

    $ brew install nginx
    

    After installation, check if Nginx is running:

    $ sudo systemctl status nginx
    

    If it’s not running, start and enable it:

    $ sudo systemctl start nginx
    $ sudo systemctl enable nginx
    

Initialize a New Go Project

First, create a new directory for the project and initialize a Go module:

$ cd /var/www/
$ mkdir go-api && cd go-api
Enter fullscreen mode Exit fullscreen mode
$ go mod init example.com/go-api
Enter fullscreen mode Exit fullscreen mode

This command creates a Go module named example.com/go-api, which helps manage dependencies.

Create a Simple API

Now, create a new file main.go and add the following code:

$ vim main.go
Enter fullscreen mode Exit fullscreen mode
package main

import (
        "fmt"
        "net/http"
)

func handler(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Content-Type", "text/plain")
        fmt.Fprintln(w, "Simple Go API")
}

func main() {
        http.HandleFunc("/", handler)
        fmt.Println("Server started at :8080")
        http.ListenAndServe(":8080", nil)
}
Enter fullscreen mode Exit fullscreen mode

Compile and run the Go server:

$ go run main.go
Enter fullscreen mode Exit fullscreen mode

If successful, you should see this message in the terminal:

Server started at :8080
Enter fullscreen mode Exit fullscreen mode

Now test the API using curl:

$ curl localhost:8080


Simple Go API
Enter fullscreen mode Exit fullscreen mode

Create a Simple API with ASCII Text Response (Optional)

First, install the go-figure package:

$ go get github.com/common-nighthawk/go-figure
Enter fullscreen mode Exit fullscreen mode

Now, modify main.go to generate an ASCII text response dynamically:

package main

import (
    "fmt"
    "net/http"

    "github.com/common-nighthawk/go-figure"
)

func handler(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "text/plain")
    asciiArt := figure.NewFigure("Simple Go API", "", true).String()
    fmt.Fprintln(w, asciiArt)
}

func main() {
    http.HandleFunc("/", handler)
    fmt.Println("Server started at :8080")
    http.ListenAndServe(":8080", nil)
}
Enter fullscreen mode Exit fullscreen mode
$ curl localhost:8080

  ____    _                       _             ____                _      ____    ___
 / ___|  (_)  _ __ ___    _ __   | |   ___     / ___|   ___        / \    |  _ \  |_ _|
 \___ \  | | | '_ ` _ \  | '_ \  | |  / _ \   | |  _   / _ \      / _ \   | |_) |  | |
  ___) | | | | | | | | | | |_) | | | |  __/   | |_| | | (_) |    / ___ \  |  __/   | |
 |____/  |_| |_| |_| |_| | .__/  |_|  \___|    \____|  \___/    /_/   \_\ |_|     |___|
                         |_|
Enter fullscreen mode Exit fullscreen mode

Running the API as a Background Service with Supervisor

Create a Supervisor Configuration for the Go API

Create a new Supervisor config file:

$ sudo vim /etc/supervisor/conf.d/go-api.conf
Enter fullscreen mode Exit fullscreen mode

Add the following configuration:

[program:go-api]
process_name=%(program_name)s_%(process_num)02d
directory=/var/www/go-api
command=bash -c 'cd /var/www/go-api && ./main'
autostart=true
autorestart=true
user=www-data
redirect_stderr=true
stderr_logfile=/var/log/go-api.err.log
stdout_logfile=/var/log/go-api.out.log
Enter fullscreen mode Exit fullscreen mode

Explanation:

directory=/var/www/go-api → The working directory of the Go API.
command=bash -c 'cd /var/www/go-api && ./main' → Runs the API.
autostart=true → Starts automatically on system boot.
autorestart=true → Restarts if the process crashes.
user=www-data → Runs as the www-data user (adjust as needed).
redirect_stderr=true → Redirects error logs to stdout.
stdout_logfile=/var/log/go-api.out.log → Standard output log file.
stderr_logfile=/var/go-api.err.log → Error log file.

Now, we need build the Go API:

$ go build -o main .
Enter fullscreen mode Exit fullscreen mode

Ensure the directory and binary have the correct permissions:

$ sudo chown -R www-data:www-data /var/www/go-api
$ sudo chmod 775 /var/www/go-api/main
Enter fullscreen mode Exit fullscreen mode

Apply the Supervisor Configuration

Reload Supervisor and start the service:

$ sudo supervisorctl reread
$ sudo supervisorctl update
$ sudo supervisorctl start go-api:*
Enter fullscreen mode Exit fullscreen mode

Check the service status:

$ sudo supervisorctl avail
go-api:go-api_00                 in use    auto      999:999
Enter fullscreen mode Exit fullscreen mode
$ sudo supervisorctl status go-api:*

go-api:go-api_00                 RUNNING   pid 198867, uptime 0:01:52
Enter fullscreen mode Exit fullscreen mode

Check Logs and Debugging

If the API is not running, check the logs:

cat /var/log/go-api.out.log
cat /var/log/go-api.err.log
Enter fullscreen mode Exit fullscreen mode

Or use Supervisor’s built-in log viewer:

$ sudo supervisorctl tail -f go-api:go-api_00

==> Press Ctrl-C to exit <==
Server started at :8080
Enter fullscreen mode Exit fullscreen mode

Setting Up Nginx as a Reverse Proxy for the API

Create a new configuration file:

$ sudo vim /etc/nginx/sites-available/go-api
Enter fullscreen mode Exit fullscreen mode
server {
    server_name _;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
    }

    error_log /var/log/nginx/go-api_error.log;
    access_log /var/log/nginx/go-api_access.log;
}
Enter fullscreen mode Exit fullscreen mode

Create a symbolic link to enable the site:

$ sudo ln -s /etc/nginx/sites-available/go-api /etc/nginx/sites-enabled/
Enter fullscreen mode Exit fullscreen mode

Test the configuration:

$ nginx -t

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
Enter fullscreen mode Exit fullscreen mode

If the test is successful, restart Nginx:

$ sudo systemctl restart nginx
Enter fullscreen mode Exit fullscreen mode

Now, you can access your Go API using:

  • Localhost (if running locally)
$ curl http://localhost

  ____    _                       _             ____                _      ____    ___
 / ___|  (_)  _ __ ___    _ __   | |   ___     / ___|   ___        / \    |  _ \  |_ _|
 \___ \  | | | '_ ` _ \  | '_ \  | |  / _ \   | |  _   / _ \      / _ \   | |_) |  | |
  ___) | | | | | | | | | | |_) | | | |  __/   | |_| | | (_) |    / ___ \  |  __/   | |
 |____/  |_| |_| |_| |_| | .__/  |_|  \___|    \____|  \___/    /_/   \_\ |_|     |___|
                         |_|
Enter fullscreen mode Exit fullscreen mode
  • Server’s Public IP (if running on a VPS or remote server)
$ curl http://YOUR_SERVER_IP
Enter fullscreen mode Exit fullscreen mode

Note: If you want to access your Go API using a custom domain instead of an IP address, you need to purchase a domain, configure its DNS to point to your server’s IP, and update your Nginx configuration accordingly. For better security, it's recommended to set up HTTPS using Let's Encrypt.

Conclusion

In this guide, we deployed a simple Go API using Supervisor to manage the process ensuring automatic restarts and efficient request handling also Nginx as a reverse proxy. Thank you for reading, and good luck with your deployment! 🚀

Top comments (4)

Collapse
 
gad31 profile image
Gad

Hey, thanks for the write up. why not using supervisor inside a container ?

Collapse
 
danielcristho profile image
Daniel Pepuho • Edited

That's a great idea, but i wonder why we should use supervisor inside a container. Containers are designed to run a single process efficiently, so i think adding supervisor for process management can introduce unnecessary complexity. However, if you have multiple lightweight processes in a single container and need supervisor, it can still work it just might not be the most efficient approach.

Collapse
 
gad31 profile image
Gad

Yes, thanks. my eyes was caught by the autorestart parameter in supervisor but I had forgotten you can change the container restart policy with the --restart flag. Thanks for your answer

Thread Thread
 
danielcristho profile image
Daniel Pepuho

Woah, that’s a great insight. Thanks for sharing.