Mastering Go Backend Deployment: Build, Run, and Rollback Scripts Explained

This guide walks through a complete set of Bash scripts for building, starting, stopping, rolling back, and monitoring a Go backend service, explains signal handling in Go, and provides a curated list of useful open‑source Go projects and libraries.

Go Development Architecture Practice
Go Development Architecture Practice
Go Development Architecture Practice
Mastering Go Backend Deployment: Build, Run, and Rollback Scripts Explained

Introduction

Deploying a Go backend reliably requires a set of scripts that can build, start, stop, roll back, and view logs of the service. The following examples show how to organize these scripts in a single directory and automate common deployment tasks.

Directory Layout

Create the following files in one folder:

bin        # Directory for compiled binaries after each build
app.log    # Log file for runtime output
log.sh     # Tail the last 200 lines of the log
build.sh   # Build the Go project
run.sh     # Run a specific binary in background
start.sh   # Start the latest version, backing up the previous one
shutdown.sh# Stop the running service
rollback.sh# Roll back to the previous version

Script Details

log.sh

tail -f -n 200 app.log  # Show the last 200 lines of the log in real time

build.sh

#!/bin/bash
baseProjectDir="/go/src/monitor"   # Project path inside GOPATH
targetDir="bin"                     # Directory for compiled binaries
branch="master"

pwd=$(pwd)
targetFile=$(basename $pwd)
buildPkg="monitor"
buildResult=""

if [ -n "$1" ]; then
  branch="$1"
  echo "Switch branch to ${branch}"
else
  echo "Building Branch: ${branch}"
fi

gitPull() {
  pushd .
  cd "$baseProjectDir"
  git checkout "$branch"
  git pull
  popd
}

goBuild() {
  buildResult=$(go build -o "${targetDir}/${targetFile}" "${buildPkg}" 2>&1)
  if [ -z "$buildResult" ]; then
    buildResult="success"
  fi
}

gitPull
goBuild

if [ "$buildResult" = "success" ]; then
  cp "${targetDir}/${targetFile}" "${targetFile}-new"
  chmod +x "${targetFile}-new"
else
  echo "build error ${buildResult}"
  exit 1
fi

echo "All Complete"

run.sh

#!/bin/bash
# Run the binary in background and redirect output to app.log
nohup ./$1 >> app.log &

start.sh

#!/bin/bash
pwd=$(pwd)
target=$(basename $pwd)
# Kill existing process if any
pid=$(ps -C ${target} -o pid=)
if [ -n "$pid" ]; then
  echo "Stopping old version, PID: ${pid}"
  if [ "$1" = "-f" ]; then
    echo "Force shutdown..."
    kill -9 $(ps -C ${target} -o pid=)
  else
    kill -s 2 $(ps -C ${target} -o pid=)
  fi
  # Wait for process to exit
  while [ -n "$(ps -C ${target} -o pid=)" ]; do
    sleep 1
done
fi
# Upgrade if new binary exists
if [ -f "${target}-new" ]; then
  echo "Upgrading..."
  if [ -f "${target}-backup" ]; then
    backupdt=$(date +%Y%m%d-%H)
    mv "${target}-backup" "${target}-backup-${backupdt}"
  fi
  mv "${target}" "${target}-backup"
  mv "${target}-new" "${target}"
  echo "Upgrade Complete"
fi
# Start the new version
echo "Starting..."
./run.sh ${target}
echo "Done"

shutdown.sh

#!/bin/bash
pwd=$(pwd)
target=$(basename $pwd)
pid=$(ps -C ${target} -o pid=)
if [ -n "$pid" ]; then
  echo "Stopping old version, PID: ${pid}"
  if [ "$1" = "-f" ]; then
    echo "Force shutdown..."
    kill -9 $(ps -C ${target} -o pid=)
  else
    kill -s 2 $(ps -C ${target} -o pid=)
  fi
  while [ -n "$(ps -C ${target} -o pid=)" ]; do
    sleep 1
done
fi
echo "Done"

rollback.sh

#!/bin/bash
pwd=$(pwd)
target=$(basename $pwd)
# Kill running program
pid=$(ps -C ${target} -o pid=)
if [ -n "$pid" ]; then
  echo "Stopping old version, PID: ${pid}"
  if [ "$1" = "-f" ]; then
    echo "Force shutdown..."
    kill $(ps -C ${target} -o pid=)
  else
    kill -s 2 $(ps -C ${target} -o pid=)
  fi
  while [ -n "$(ps -C ${target} -o pid=)" ]; do
    sleep 1
done
fi
# Rollback if backup exists
if [ -f "${target}-backup" ]; then
  echo "Rolling back..."
  rm -f "${target}"
  mv "${target}-backup" "${target}"
  echo "Rollback Complete"
fi
# Start the restored version
echo "Starting..."
./run.sh ${target}
echo "Done"

Signal Handling in Go

When using kill -s 2 (SIGINT), the Go program should listen for the interrupt signal to exit gracefully:

package main

import (
    "log"
    "os"
    "os/signal"
    "github.com/robfig/cron"
)

func main() {
    i := 0
    c := cron.New()
    spec := "*/3 * * * * ?"
    c.AddFunc(spec, func() {
        i++
        log.Println("cron running:", i)
    })
    c.Start()
    go signalListen()
    select {}
}

func signalListen() {
    c := make(chan os.Signal)
    signal.Notify(c, os.Interrupt, os.Kill)
    for {
        <-c
        os.Exit(0)
    }
}

Additional Go Open‑Source Resources

The author also compiled a list of useful Go projects and libraries for further learning:

Console progress bar

NutsDB

Microsoft Go tutorial

Batch rename tool

Dapr

Didi open‑source projects

Go generics

Chinese open‑source monitoring tools

SQL utilities

sqlx

Go form validator

Eye‑following mouse cursor

Fiber browser

Frontend‑to‑Go framework preferences

Go distributed file system

Image server

Go live‑streaming project

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

AutomationdeploymentGoShell Scripts
Go Development Architecture Practice
Written by

Go Development Architecture Practice

Daily sharing of Golang-related technical articles, practical resources, language news, tutorials, real-world projects, and more. Looking forward to growing together. Let's go!

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.