Golang App as a Systemd Service

Published by Oga Ajima on 2018-07-19

You might have an idea for a go REST api where you don't store configuration within the app but rather get it from an environment variable, how to do this? Well, Kelsey Hightower's envconfig has a library that does that very nicely. Also want to run it as a service, well there's systemd for that. Example app and systemd service files below. Created a simple database called test and created a table within that with userinfo that holds username, firstname, lastname, and team. Then export config info, I use /etc/environment, this way it is available system wide

Database Schema - Table name=userinfo

 id | username | firstname | lastname | team
----+----------+-----------+----------+--------------

Env variables

TEST_DB_USERNAME='tester'
TEST_DB_PASSWORD='secretpassword'
TEST_DB_NAME='databasename'

I exported these variables in /etc/environment

package main

import (
        "database/sql"
        "fmt"
        "log"
        "net/http"

        "github.com/kelseyhightower/envconfig"
        _ "github.com/lib/pq"

)

type DB struct {
 Username string
 Password string
 Name     string
}

func main() {
  http.HandleFunc("/", sayName) // set router
  err := http.ListenAndServe(":8080", nil) // set listen port
  if err != nil {
    log.Fatal("ListenAndServe: ", err)
  }
}

func sayName(w http.ResponseWriter, r *http.Request) {
  var config DB
  err := envconfig.Process("test_db", &config)
  checkErr(err)

  connectionString := fmt.Sprintf("user=%s password=%s dbname=%s", config.Username, config.Password, config.Name)

  db, err := sql.Open("postgres", connectionString)
  checkErr(err)

  err = db.Ping()
  checkErr(err)

  rows, err := db.Query("SELECT username,firstname,lastname,team FROM userinfo")
  checkErr(err)

  for rows.Next() {
    var username string
    var firstname string
    var lastname string
    var team string
    err = rows.Scan(&username, &firstname, &lastname, &team)
    checkErr(err)
    fmt.Printf("username | firstname | lastname | team \n")
    fmt.Printf("---------+-----------+----------+------\n")
    fmt.Printf("%8v | %9v | %8v | %4v\n", username, firstname, lastname, team)
  }
}

func checkErr(err error) {
  if err != nil {
    log.Fatal(err.Error())
  }
}

Console output

curl localhost:8080

username | firstname | lastname | team
---------+-----------+----------+------
scholesy |      Paul |  Scholes | Man Utd

systemd service file

[Unit]
Description=tester service

StartLimitIntervalSec=60
 
[Service]
Type=simple

EnvironmentFile=-/etc/environment

Restart=on-failure
RestartSec=10

ExecStart=/home/ubuntu/go/src/tester/tester
 
[Install]
WantedBy=multi-user.target

Enable and start service

sudo mv tester.service /lib/systemd/system/.
sudo chmod 755 /lib/systemd/system/tester.service
sudo systemctl enable tester.service
sudo systemctl start tester

Further reading