Modern backend development is overly complex. In order to deploy even simplest Golang web service now you’d probably going to need:
Go makes many things straightforward, yet there’s still a lot of unnecessary complexity. Some companies even have dedicated “platform engineering” teams to deal with that, which speaks volumes about the issue.
Web services with dynamic content is a problem solved long time ago. Does it really need to be that complex today? Is there easier way? I think, there is.
The way I’m suggesting is better explained with illustrated example, going through a complete step-by-step guide.
Just get a Linux server. In this guide I use one with Ubuntu 24.04.
Let’s go with tried and true Apache 2.
apt update && apt install -y apache2
a2enmod cgid
Edit file /etc/apache2/conf-enabled/serve-cgi-bin.conf and make sure it looks like this:
<IfModule mod_alias.c>
<IfModule mod_cgi.c>
Define ENABLE_USR_LIB_CGI_BIN
</IfModule>
<IfModule mod_cgid.c>
Define ENABLE_USR_LIB_CGI_BIN
</IfModule>
<IfDefine ENABLE_USR_LIB_CGI_BIN>
ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/
<Directory "/usr/lib/cgi-bin">
AllowOverride None
SetEnv GOCACHE /var/tmp/.www-cache/go-build
Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
Require all granted
</Directory>
</IfDefine>
</IfModule>
Note the added line SetEnv GOCACHE /var/tmp/.www-cache/go-build.
Finally, restart the web server to apply configuration:
systemctl restart apache2
First we need Golang itself.
apt install -y golang-go
Then we need a binfmt support to enable Golang scripts:
go install github.com/Snawoot/go-binfmt@latest
install /root/go/bin/go-binfmt /usr/local/bin
cat > /etc/systemd/system/go-binfmt.service <<'EOF'
[Unit]
Description=Register go-binfmt support
After=proc-sys-fs-binfmt_misc.mount
Wants=proc-sys-fs-binfmt_misc.mount
[Service]
Type=oneshot
ExecStart=/usr/local/bin/go-binfmt -register
ExecStop=/usr/local/bin/go-binfmt -unregister
RemainAfterExit=yes
User=root
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable --now go-binfmt.service
Let’s greet the world with a simple “Hello World” Go script!
Create /usr/lib/cgi-bin/hello.go with following content:
package main
import (
"fmt"
"net/http"
"net/http/cgi"
)
func main() {
err := cgi.Serve(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/html; charset=utf-8")
fmt.Fprintln(w, "<!DOCTYPE html>")
fmt.Fprintln(w, "<html><body><head><title>Hello World</title></head>")
fmt.Fprintln(w, "<h1 style=\"color: green;\">Hello, World!</h1>")
fmt.Fprintln(w, "<p>This page was generated by a Go script running on Apache2 (Ubuntu 24.04).</p>")
fmt.Fprintln(w, "</body><html>")
}))
if err != nil {
fmt.Println(err)
}
}
And make this script executable:
chmod +x /usr/lib/cgi-bin/hello.go
That’s it! Now your web application is accessible on URL http://SERVER_IP/cgi-bin/hello.go.