Skip to content
Latchkey

CI/CD for a Go Web Service with GitHub Actions

A Go service with a database runs go test against real Postgres, then builds a single static binary to ship.

This recipe sets up GitHub Actions for a Go web service. It vets, lints, runs tests against a Postgres service container, builds a binary, and deploys on main.

What the pipeline does

  • go vet
  • Lint with golangci-lint
  • go test against Postgres
  • go build static binary
  • Deploy on main

The workflow

setup-go caches modules and build artifacts automatically.

.github/workflows/ci.yml
name: CI
on: [push, pull_request]
jobs:
  test:
    runs-on: ubuntu-latest
    services:
      postgres:
        image: postgres:16
        env:
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: app_test
        ports: ["5432:5432"]
        options: >-
          --health-cmd "pg_isready -U postgres"
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
    env:
      DATABASE_URL: postgres://postgres:postgres@localhost:5432/app_test?sslmode=disable
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-go@v5
        with:
          go-version: '1.22'
          cache: true
      - run: go vet ./...
      - run: go test -race ./...
  deploy:
    needs: test
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-go@v5
        with:
          go-version: '1.22'
          cache: true
      - run: CGO_ENABLED=0 go build -o app ./cmd/server
      - run: ./deploy.sh app

Testing against a database

The postgres service is at localhost:5432; pass DATABASE_URL with sslmode=disable to your test setup. Apply migrations (golang-migrate, goose, or your own) before go test. Use -race to catch data races, which matters for concurrent HTTP handlers.

Deploying

CGO_ENABLED=0 go build produces a static binary with no runtime dependencies - ideal for a scratch or distroless container, or a straight copy to a VPS over SSH. Run it under systemd or in a container behind a load balancer.

Key takeaways

  • Run go test -race to catch concurrency bugs in HTTP handlers.
  • Build with CGO_ENABLED=0 for a portable static binary.
  • Apply migrations against the postgres service before tests run.

Related guides

Run this faster and cheaper on Latchkey managed runners. Start free →