0b
+
+
+
rocket
+
+
htmx
+
ฮป
kali
clion
nim
+
0x
+
qwik
โˆซ
https
|>
+
+
+
+
+
+
firebase
+
+
+
+
+
zorin
โˆฉ
c
+
elixir
+
solidity
laravel
rb
+
nvim
[]
yarn
+
+
elasticsearch
+
pnpm
+
+
+
+
pycharm
hugging
+
fortran
bitbucket
+
fortran
+
+
+
alpine
+
lit
influxdb
vscode
+
+
c
dart
vault
riot
choo
+
ubuntu
+
+
smtp
+
erlang
=>
+
rollup
+
+
rest
Back to Blog
alpine-linux golang web-development

Setting Up Go Web Applications on Alpine Linux ๐Ÿš€

Published Jun 13, 2025

Deploy production-ready Go web applications on Alpine Linux. Learn to configure Go development environment, build web services, implement databases, and optimize for performance and security.

5 min read
0 views
Table of Contents

Alpine Linux is the perfect platform for Go applications due to its minimal footprint and security features. This guide will help you set up a complete Go development and deployment environment, from basic web servers to production-ready microservices.

Table of Contents

Prerequisites

Before starting, ensure you have:

  • Alpine Linux installed and updated
  • Root or sudo access
  • Basic understanding of Go programming
  • 2GB+ RAM for development
  • Internet connection for package downloads
  • Git installed for version control

Installing Go on Alpine

Step 1: Install Go from Packages

# Update package repository
apk update

# Install Go
apk add go

# Install additional development tools
apk add git make gcc musl-dev

# Verify installation
go version

Step 2: Install Latest Go Version

# Download latest Go (check https://golang.org/dl/ for latest version)
cd /tmp
wget https://go.dev/dl/go1.21.0.linux-amd64.tar.gz

# Remove existing Go installation
rm -rf /usr/local/go

# Extract new version
tar -C /usr/local -xzf go1.21.0.linux-amd64.tar.gz

# Set up environment variables
cat >> /etc/profile << 'EOF'
export PATH=$PATH:/usr/local/go/bin
export GOPATH=$HOME/go
export PATH=$PATH:$GOPATH/bin
EOF

# Apply changes
source /etc/profile

# Verify installation
go version

Step 3: Configure Go Workspace

# Create Go workspace
mkdir -p ~/go/{bin,src,pkg}

# Set up user environment
cat >> ~/.bashrc << 'EOF'
export GOPATH=$HOME/go
export PATH=$PATH:$GOPATH/bin
export GO111MODULE=on
export GOPROXY=https://proxy.golang.org,direct
EOF

source ~/.bashrc

Development Environment Setup

Step 1: Install Development Tools

# Install essential tools
go install golang.org/x/tools/gopls@latest
go install github.com/go-delve/delve/cmd/dlv@latest
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
go install github.com/air-verse/air@latest

# Install database drivers
go get -u github.com/lib/pq
go get -u github.com/go-sql-driver/mysql
go get -u github.com/mattn/go-sqlite3

Step 2: Create Development Scripts

# Create project initialization script
cat > /usr/local/bin/go-init-project << 'EOF'
#!/bin/sh
# Initialize new Go project

PROJECT_NAME=$1
if [ -z "$PROJECT_NAME" ]; then
    echo "Usage: go-init-project <project-name>"
    exit 1
fi

# Create project structure
mkdir -p $PROJECT_NAME/{cmd,internal,pkg,api,web/static,web/templates,configs,scripts,test}
cd $PROJECT_NAME

# Initialize Go module
go mod init github.com/$(whoami)/$PROJECT_NAME

# Create main.go
cat > cmd/main.go << 'EOG'
package main

import (
    "fmt"
    "log"
    "net/http"
    "os"
)

func main() {
    port := os.Getenv("PORT")
    if port == "" {
        port = "8080"
    }

    http.HandleFunc("/", homeHandler)
    http.HandleFunc("/health", healthHandler)

    log.Printf("Server starting on port %s", port)
    if err := http.ListenAndServe(":"+port, nil); err != nil {
        log.Fatal(err)
    }
}

func homeHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Welcome to %s!", "PROJECT_NAME")
}

func healthHandler(w http.ResponseWriter, r *http.Request) {
    w.WriteHeader(http.StatusOK)
    fmt.Fprintln(w, "OK")
}
EOG

# Create Makefile
cat > Makefile << 'EOM'
.PHONY: build run test clean

build:
	go build -o bin/app cmd/main.go

run:
	go run cmd/main.go

test:
	go test -v ./...

clean:
	rm -rf bin/

docker-build:
	docker build -t $(PROJECT_NAME) .

docker-run:
	docker run -p 8080:8080 $(PROJECT_NAME)
EOM

# Create .gitignore
cat > .gitignore << 'EOI'
# Binaries
*.exe
*.exe~
*.dll
*.so
*.dylib
bin/

# Test binary
*.test

# Output
*.out

# Go workspace
go.work

# Environment
.env
*.env

# IDE
.idea/
.vscode/
*.swp
*.swo
EOI

# Create README
cat > README.md << 'EOR'
# PROJECT_NAME

## Description
Go web application on Alpine Linux

## Quick Start
\`\`\`bash
make build
make run
\`\`\`

## Development
\`\`\`bash
air # Hot reload
make test # Run tests
\`\`\`
EOR

sed -i "s/PROJECT_NAME/$PROJECT_NAME/g" cmd/main.go
sed -i "s/PROJECT_NAME/$PROJECT_NAME/g" README.md

echo "Project $PROJECT_NAME initialized successfully!"
EOF

chmod +x /usr/local/bin/go-init-project

Step 3: Hot Reload Configuration

# Create air configuration
cat > .air.toml << 'EOF'
root = "."
testdata_dir = "testdata"
tmp_dir = "tmp"

[build]
  args_bin = []
  bin = "./tmp/main"
  cmd = "go build -o ./tmp/main ./cmd/main.go"
  delay = 1000
  exclude_dir = ["assets", "tmp", "vendor", "testdata"]
  exclude_file = []
  exclude_regex = ["_test.go"]
  exclude_unchanged = false
  follow_symlink = false
  full_bin = ""
  include_dir = []
  include_ext = ["go", "tpl", "tmpl", "html"]
  kill_delay = "0s"
  log = "build-errors.log"
  send_interrupt = false
  stop_on_error = true

[color]
  app = ""
  build = "yellow"
  main = "magenta"
  runner = "green"
  watcher = "cyan"

[log]
  time = false

[misc]
  clean_on_exit = false

[screen]
  clear_on_rebuild = false
EOF

Creating a Basic Web Application

Step 1: Web Server with Routing

// File: cmd/web/main.go
package main

import (
    "context"
    "encoding/json"
    "html/template"
    "log"
    "net/http"
    "os"
    "os/signal"
    "syscall"
    "time"

    "github.com/gorilla/mux"
    "github.com/gorilla/handlers"
)

type Server struct {
    router *mux.Router
    logger *log.Logger
}

func NewServer() *Server {
    return &Server{
        router: mux.NewRouter(),
        logger: log.New(os.Stdout, "[SERVER] ", log.LstdFlags),
    }
}

func (s *Server) ConfigureRoutes() {
    // Static files
    s.router.PathPrefix("/static/").Handler(
        http.StripPrefix("/static/", http.FileServer(http.Dir("./web/static/"))),
    )

    // API routes
    api := s.router.PathPrefix("/api/v1").Subrouter()
    api.Use(s.jsonMiddleware)
    api.HandleFunc("/users", s.handleUsers).Methods("GET")
    api.HandleFunc("/users/{id}", s.handleUser).Methods("GET")
    api.HandleFunc("/users", s.createUser).Methods("POST")

    // Web routes
    s.router.HandleFunc("/", s.handleHome).Methods("GET")
    s.router.HandleFunc("/about", s.handleAbout).Methods("GET")

    // Health check
    s.router.HandleFunc("/health", s.handleHealth).Methods("GET")
}

func (s *Server) jsonMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Content-Type", "application/json")
        next.ServeHTTP(w, r)
    })
}

func (s *Server) handleHome(w http.ResponseWriter, r *http.Request) {
    tmpl := template.Must(template.ParseFiles("web/templates/layout.html", "web/templates/home.html"))
    data := struct {
        Title   string
        Message string
    }{
        Title:   "Home",
        Message: "Welcome to Go on Alpine Linux!",
    }
    tmpl.Execute(w, data)
}

func (s *Server) handleAbout(w http.ResponseWriter, r *http.Request) {
    tmpl := template.Must(template.ParseFiles("web/templates/layout.html", "web/templates/about.html"))
    data := struct {
        Title string
    }{
        Title: "About",
    }
    tmpl.Execute(w, data)
}

func (s *Server) handleHealth(w http.ResponseWriter, r *http.Request) {
    response := map[string]string{
        "status": "healthy",
        "time":   time.Now().Format(time.RFC3339),
    }
    json.NewEncoder(w).Encode(response)
}

func (s *Server) handleUsers(w http.ResponseWriter, r *http.Request) {
    users := []map[string]interface{}{
        {"id": 1, "name": "John Doe", "email": "[email protected]"},
        {"id": 2, "name": "Jane Smith", "email": "[email protected]"},
    }
    json.NewEncoder(w).Encode(users)
}

func (s *Server) handleUser(w http.ResponseWriter, r *http.Request) {
    vars := mux.Vars(r)
    id := vars["id"]
    
    user := map[string]interface{}{
        "id":    id,
        "name":  "John Doe",
        "email": "[email protected]",
    }
    json.NewEncoder(w).Encode(user)
}

func (s *Server) createUser(w http.ResponseWriter, r *http.Request) {
    var user map[string]interface{}
    if err := json.NewDecoder(r.Body).Decode(&user); err != nil {
        http.Error(w, err.Error(), http.StatusBadRequest)
        return
    }
    
    user["id"] = 3
    user["created_at"] = time.Now()
    
    w.WriteHeader(http.StatusCreated)
    json.NewEncoder(w).Encode(user)
}

func (s *Server) Run(addr string) error {
    srv := &http.Server{
        Addr:         addr,
        Handler:      handlers.LoggingHandler(os.Stdout, s.router),
        ReadTimeout:  15 * time.Second,
        WriteTimeout: 15 * time.Second,
        IdleTimeout:  60 * time.Second,
    }

    // Graceful shutdown
    go func() {
        sigint := make(chan os.Signal, 1)
        signal.Notify(sigint, os.Interrupt, syscall.SIGTERM)
        <-sigint

        s.logger.Println("Shutting down server...")
        
        ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
        defer cancel()
        
        if err := srv.Shutdown(ctx); err != nil {
            s.logger.Printf("Server forced to shutdown: %v", err)
        }
    }()

    s.logger.Printf("Server starting on %s", addr)
    return srv.ListenAndServe()
}

func main() {
    server := NewServer()
    server.ConfigureRoutes()
    
    port := os.Getenv("PORT")
    if port == "" {
        port = "8080"
    }
    
    if err := server.Run(":" + port); err != nil && err != http.ErrServerClosed {
        log.Fatal(err)
    }
}

Step 2: HTML Templates

<!-- File: web/templates/layout.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{{.Title}} - Go Web App</title>
    <link rel="stylesheet" href="/static/css/style.css">
</head>
<body>
    <nav>
        <ul>
            <li><a href="/">Home</a></li>
            <li><a href="/about">About</a></li>
            <li><a href="/api/v1/users">API</a></li>
        </ul>
    </nav>
    
    <main>
        {{block "content" .}}{{end}}
    </main>
    
    <footer>
        <p>&copy; 2024 Go Web Application on Alpine Linux</p>
    </footer>
    
    <script src="/static/js/app.js"></script>
</body>
</html>
<!-- File: web/templates/home.html -->
{{define "content"}}
<h1>{{.Message}}</h1>
<p>This is a Go web application running on Alpine Linux.</p>

<div id="users">
    <h2>Users</h2>
    <button onclick="loadUsers()">Load Users</button>
    <div id="userList"></div>
</div>
{{end}}

Step 3: Static Assets

/* File: web/static/css/style.css */
body {
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
    line-height: 1.6;
    margin: 0;
    padding: 0;
    background-color: #f4f4f4;
}

nav {
    background-color: #333;
    color: white;
    padding: 1rem;
}

nav ul {
    list-style: none;
    padding: 0;
    margin: 0;
    display: flex;
}

nav ul li {
    margin-right: 1rem;
}

nav ul li a {
    color: white;
    text-decoration: none;
}

main {
    max-width: 1200px;
    margin: 2rem auto;
    padding: 0 1rem;
    background-color: white;
    border-radius: 5px;
    box-shadow: 0 0 10px rgba(0,0,0,0.1);
    min-height: 400px;
    padding: 2rem;
}

footer {
    text-align: center;
    padding: 1rem;
    background-color: #333;
    color: white;
    position: fixed;
    bottom: 0;
    width: 100%;
}
// File: web/static/js/app.js
async function loadUsers() {
    try {
        const response = await fetch('/api/v1/users');
        const users = await response.json();
        
        const userList = document.getElementById('userList');
        userList.innerHTML = '<ul>' + 
            users.map(user => `<li>${user.name} - ${user.email}</li>`).join('') +
            '</ul>';
    } catch (error) {
        console.error('Error loading users:', error);
    }
}

Database Integration

Step 1: PostgreSQL Integration

// File: internal/database/postgres.go
package database

import (
    "database/sql"
    "fmt"
    "log"
    "time"

    _ "github.com/lib/pq"
)

type Config struct {
    Host     string
    Port     int
    User     string
    Password string
    DBName   string
    SSLMode  string
}

type DB struct {
    *sql.DB
}

func NewConnection(cfg Config) (*DB, error) {
    dsn := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=%s",
        cfg.Host, cfg.Port, cfg.User, cfg.Password, cfg.DBName, cfg.SSLMode)
    
    db, err := sql.Open("postgres", dsn)
    if err != nil {
        return nil, err
    }
    
    // Configure connection pool
    db.SetMaxOpenConns(25)
    db.SetMaxIdleConns(5)
    db.SetConnMaxLifetime(5 * time.Minute)
    
    // Test connection
    if err := db.Ping(); err != nil {
        return nil, err
    }
    
    return &DB{db}, nil
}

func (db *DB) Migrate() error {
    query := `
    CREATE TABLE IF NOT EXISTS users (
        id SERIAL PRIMARY KEY,
        username VARCHAR(50) UNIQUE NOT NULL,
        email VARCHAR(100) UNIQUE NOT NULL,
        password_hash VARCHAR(255) NOT NULL,
        created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
        updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
    );

    CREATE TABLE IF NOT EXISTS sessions (
        id VARCHAR(64) PRIMARY KEY,
        user_id INTEGER REFERENCES users(id) ON DELETE CASCADE,
        expires_at TIMESTAMP NOT NULL,
        created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
    );

    CREATE INDEX IF NOT EXISTS idx_sessions_user_id ON sessions(user_id);
    CREATE INDEX IF NOT EXISTS idx_sessions_expires_at ON sessions(expires_at);
    `
    
    _, err := db.Exec(query)
    return err
}

Step 2: Repository Pattern

// File: internal/repository/user.go
package repository

import (
    "database/sql"
    "errors"
    "time"
)

type User struct {
    ID           int       `json:"id"`
    Username     string    `json:"username"`
    Email        string    `json:"email"`
    PasswordHash string    `json:"-"`
    CreatedAt    time.Time `json:"created_at"`
    UpdatedAt    time.Time `json:"updated_at"`
}

type UserRepository struct {
    db *sql.DB
}

func NewUserRepository(db *sql.DB) *UserRepository {
    return &UserRepository{db: db}
}

func (r *UserRepository) Create(user *User) error {
    query := `
        INSERT INTO users (username, email, password_hash)
        VALUES ($1, $2, $3)
        RETURNING id, created_at, updated_at
    `
    
    err := r.db.QueryRow(query, user.Username, user.Email, user.PasswordHash).
        Scan(&user.ID, &user.CreatedAt, &user.UpdatedAt)
    
    return err
}

func (r *UserRepository) GetByID(id int) (*User, error) {
    user := &User{}
    query := `
        SELECT id, username, email, password_hash, created_at, updated_at
        FROM users
        WHERE id = $1
    `
    
    err := r.db.QueryRow(query, id).Scan(
        &user.ID, &user.Username, &user.Email, 
        &user.PasswordHash, &user.CreatedAt, &user.UpdatedAt,
    )
    
    if err == sql.ErrNoRows {
        return nil, errors.New("user not found")
    }
    
    return user, err
}

func (r *UserRepository) GetByUsername(username string) (*User, error) {
    user := &User{}
    query := `
        SELECT id, username, email, password_hash, created_at, updated_at
        FROM users
        WHERE username = $1
    `
    
    err := r.db.QueryRow(query, username).Scan(
        &user.ID, &user.Username, &user.Email,
        &user.PasswordHash, &user.CreatedAt, &user.UpdatedAt,
    )
    
    if err == sql.ErrNoRows {
        return nil, errors.New("user not found")
    }
    
    return user, err
}

func (r *UserRepository) Update(user *User) error {
    query := `
        UPDATE users
        SET username = $1, email = $2, updated_at = CURRENT_TIMESTAMP
        WHERE id = $3
    `
    
    _, err := r.db.Exec(query, user.Username, user.Email, user.ID)
    return err
}

func (r *UserRepository) Delete(id int) error {
    _, err := r.db.Exec("DELETE FROM users WHERE id = $1", id)
    return err
}

func (r *UserRepository) List(offset, limit int) ([]*User, error) {
    query := `
        SELECT id, username, email, password_hash, created_at, updated_at
        FROM users
        ORDER BY created_at DESC
        LIMIT $1 OFFSET $2
    `
    
    rows, err := r.db.Query(query, limit, offset)
    if err != nil {
        return nil, err
    }
    defer rows.Close()
    
    var users []*User
    for rows.Next() {
        user := &User{}
        err := rows.Scan(
            &user.ID, &user.Username, &user.Email,
            &user.PasswordHash, &user.CreatedAt, &user.UpdatedAt,
        )
        if err != nil {
            return nil, err
        }
        users = append(users, user)
    }
    
    return users, rows.Err()
}

RESTful API Development

Step 1: API Structure

// File: internal/api/handlers.go
package api

import (
    "encoding/json"
    "net/http"
    "strconv"

    "github.com/gorilla/mux"
    "github.com/yourapp/internal/repository"
)

type API struct {
    userRepo *repository.UserRepository
}

func NewAPI(userRepo *repository.UserRepository) *API {
    return &API{
        userRepo: userRepo,
    }
}

type ErrorResponse struct {
    Error string `json:"error"`
}

type SuccessResponse struct {
    Message string      `json:"message"`
    Data    interface{} `json:"data,omitempty"`
}

func (a *API) respondWithError(w http.ResponseWriter, code int, message string) {
    a.respondWithJSON(w, code, ErrorResponse{Error: message})
}

func (a *API) respondWithJSON(w http.ResponseWriter, code int, payload interface{}) {
    response, err := json.Marshal(payload)
    if err != nil {
        w.WriteHeader(http.StatusInternalServerError)
        w.Write([]byte(`{"error":"Error marshaling JSON"}`))
        return
    }
    
    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(code)
    w.Write(response)
}

// User handlers
func (a *API) GetUsers(w http.ResponseWriter, r *http.Request) {
    offset, _ := strconv.Atoi(r.URL.Query().Get("offset"))
    limit, _ := strconv.Atoi(r.URL.Query().Get("limit"))
    
    if limit == 0 || limit > 100 {
        limit = 20
    }
    
    users, err := a.userRepo.List(offset, limit)
    if err != nil {
        a.respondWithError(w, http.StatusInternalServerError, "Error fetching users")
        return
    }
    
    a.respondWithJSON(w, http.StatusOK, users)
}

func (a *API) GetUser(w http.ResponseWriter, r *http.Request) {
    vars := mux.Vars(r)
    id, err := strconv.Atoi(vars["id"])
    if err != nil {
        a.respondWithError(w, http.StatusBadRequest, "Invalid user ID")
        return
    }
    
    user, err := a.userRepo.GetByID(id)
    if err != nil {
        a.respondWithError(w, http.StatusNotFound, "User not found")
        return
    }
    
    a.respondWithJSON(w, http.StatusOK, user)
}

type CreateUserRequest struct {
    Username string `json:"username" validate:"required,min=3,max=50"`
    Email    string `json:"email" validate:"required,email"`
    Password string `json:"password" validate:"required,min=8"`
}

func (a *API) CreateUser(w http.ResponseWriter, r *http.Request) {
    var req CreateUserRequest
    if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
        a.respondWithError(w, http.StatusBadRequest, "Invalid request payload")
        return
    }
    
    // Hash password (implement proper hashing)
    passwordHash := hashPassword(req.Password)
    
    user := &repository.User{
        Username:     req.Username,
        Email:        req.Email,
        PasswordHash: passwordHash,
    }
    
    if err := a.userRepo.Create(user); err != nil {
        a.respondWithError(w, http.StatusInternalServerError, "Error creating user")
        return
    }
    
    a.respondWithJSON(w, http.StatusCreated, user)
}

func (a *API) UpdateUser(w http.ResponseWriter, r *http.Request) {
    vars := mux.Vars(r)
    id, err := strconv.Atoi(vars["id"])
    if err != nil {
        a.respondWithError(w, http.StatusBadRequest, "Invalid user ID")
        return
    }
    
    var req struct {
        Username string `json:"username"`
        Email    string `json:"email"`
    }
    
    if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
        a.respondWithError(w, http.StatusBadRequest, "Invalid request payload")
        return
    }
    
    user, err := a.userRepo.GetByID(id)
    if err != nil {
        a.respondWithError(w, http.StatusNotFound, "User not found")
        return
    }
    
    user.Username = req.Username
    user.Email = req.Email
    
    if err := a.userRepo.Update(user); err != nil {
        a.respondWithError(w, http.StatusInternalServerError, "Error updating user")
        return
    }
    
    a.respondWithJSON(w, http.StatusOK, user)
}

func (a *API) DeleteUser(w http.ResponseWriter, r *http.Request) {
    vars := mux.Vars(r)
    id, err := strconv.Atoi(vars["id"])
    if err != nil {
        a.respondWithError(w, http.StatusBadRequest, "Invalid user ID")
        return
    }
    
    if err := a.userRepo.Delete(id); err != nil {
        a.respondWithError(w, http.StatusInternalServerError, "Error deleting user")
        return
    }
    
    a.respondWithJSON(w, http.StatusOK, SuccessResponse{Message: "User deleted successfully"})
}

Step 2: API Middleware

// File: internal/middleware/middleware.go
package middleware

import (
    "context"
    "log"
    "net/http"
    "strings"
    "time"

    "github.com/google/uuid"
)

type contextKey string

const RequestIDKey contextKey = "requestID"

// RequestID middleware
func RequestID(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        requestID := r.Header.Get("X-Request-ID")
        if requestID == "" {
            requestID = uuid.New().String()
        }
        
        ctx := context.WithValue(r.Context(), RequestIDKey, requestID)
        w.Header().Set("X-Request-ID", requestID)
        
        next.ServeHTTP(w, r.WithContext(ctx))
    })
}

// Logger middleware
func Logger(logger *log.Logger) func(http.Handler) http.Handler {
    return func(next http.Handler) http.Handler {
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            start := time.Now()
            
            wrapped := &responseWriter{
                ResponseWriter: w,
                status:        http.StatusOK,
            }
            
            next.ServeHTTP(wrapped, r)
            
            logger.Printf(
                "%s %s %s %d %s %s",
                r.RemoteAddr,
                r.Method,
                r.URL.Path,
                wrapped.status,
                time.Since(start),
                r.Header.Get("User-Agent"),
            )
        })
    }
}

type responseWriter struct {
    http.ResponseWriter
    status int
}

func (rw *responseWriter) WriteHeader(code int) {
    rw.status = code
    rw.ResponseWriter.WriteHeader(code)
}

// CORS middleware
func CORS(allowedOrigins []string) func(http.Handler) http.Handler {
    return func(next http.Handler) http.Handler {
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            origin := r.Header.Get("Origin")
            
            for _, allowed := range allowedOrigins {
                if allowed == "*" || allowed == origin {
                    w.Header().Set("Access-Control-Allow-Origin", origin)
                    break
                }
            }
            
            w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
            w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Request-ID")
            w.Header().Set("Access-Control-Max-Age", "86400")
            
            if r.Method == "OPTIONS" {
                w.WriteHeader(http.StatusOK)
                return
            }
            
            next.ServeHTTP(w, r)
        })
    }
}

// RateLimit middleware
type RateLimiter struct {
    requests map[string][]time.Time
    limit    int
    window   time.Duration
}

func NewRateLimiter(limit int, window time.Duration) *RateLimiter {
    return &RateLimiter{
        requests: make(map[string][]time.Time),
        limit:    limit,
        window:   window,
    }
}

func (rl *RateLimiter) Middleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        ip := r.RemoteAddr
        now := time.Now()
        
        // Clean old requests
        if requests, exists := rl.requests[ip]; exists {
            var valid []time.Time
            for _, t := range requests {
                if now.Sub(t) < rl.window {
                    valid = append(valid, t)
                }
            }
            rl.requests[ip] = valid
        }
        
        // Check limit
        if len(rl.requests[ip]) >= rl.limit {
            http.Error(w, "Rate limit exceeded", http.StatusTooManyRequests)
            return
        }
        
        // Add current request
        rl.requests[ip] = append(rl.requests[ip], now)
        
        next.ServeHTTP(w, r)
    })
}

Authentication and Security

Step 1: JWT Authentication

// File: internal/auth/jwt.go
package auth

import (
    "errors"
    "time"

    "github.com/golang-jwt/jwt/v4"
)

type Claims struct {
    UserID   int    `json:"user_id"`
    Username string `json:"username"`
    jwt.RegisteredClaims
}

type JWTManager struct {
    secretKey     string
    tokenDuration time.Duration
}

func NewJWTManager(secretKey string, tokenDuration time.Duration) *JWTManager {
    return &JWTManager{
        secretKey:     secretKey,
        tokenDuration: tokenDuration,
    }
}

func (m *JWTManager) Generate(userID int, username string) (string, error) {
    claims := &Claims{
        UserID:   userID,
        Username: username,
        RegisteredClaims: jwt.RegisteredClaims{
            ExpiresAt: jwt.NewNumericDate(time.Now().Add(m.tokenDuration)),
            IssuedAt:  jwt.NewNumericDate(time.Now()),
            NotBefore: jwt.NewNumericDate(time.Now()),
        },
    }
    
    token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
    return token.SignedString([]byte(m.secretKey))
}

func (m *JWTManager) Verify(tokenString string) (*Claims, error) {
    token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {
        if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
            return nil, errors.New("unexpected signing method")
        }
        return []byte(m.secretKey), nil
    })
    
    if err != nil {
        return nil, err
    }
    
    claims, ok := token.Claims.(*Claims)
    if !ok || !token.Valid {
        return nil, errors.New("invalid token")
    }
    
    return claims, nil
}

Step 2: Authentication Handlers

// File: internal/auth/handlers.go
package auth

import (
    "encoding/json"
    "net/http"
    "strings"

    "golang.org/x/crypto/bcrypt"
)

type AuthService struct {
    userRepo   *repository.UserRepository
    jwtManager *JWTManager
}

func NewAuthService(userRepo *repository.UserRepository, jwtManager *JWTManager) *AuthService {
    return &AuthService{
        userRepo:   userRepo,
        jwtManager: jwtManager,
    }
}

type LoginRequest struct {
    Username string `json:"username"`
    Password string `json:"password"`
}

type LoginResponse struct {
    Token     string `json:"token"`
    ExpiresIn int64  `json:"expires_in"`
}

func (s *AuthService) Login(w http.ResponseWriter, r *http.Request) {
    var req LoginRequest
    if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
        http.Error(w, "Invalid request", http.StatusBadRequest)
        return
    }
    
    user, err := s.userRepo.GetByUsername(req.Username)
    if err != nil {
        http.Error(w, "Invalid credentials", http.StatusUnauthorized)
        return
    }
    
    if err := bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(req.Password)); err != nil {
        http.Error(w, "Invalid credentials", http.StatusUnauthorized)
        return
    }
    
    token, err := s.jwtManager.Generate(user.ID, user.Username)
    if err != nil {
        http.Error(w, "Error generating token", http.StatusInternalServerError)
        return
    }
    
    response := LoginResponse{
        Token:     token,
        ExpiresIn: int64(s.jwtManager.tokenDuration.Seconds()),
    }
    
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(response)
}

func (s *AuthService) AuthMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        authHeader := r.Header.Get("Authorization")
        if authHeader == "" {
            http.Error(w, "Missing authorization header", http.StatusUnauthorized)
            return
        }
        
        parts := strings.Split(authHeader, " ")
        if len(parts) != 2 || parts[0] != "Bearer" {
            http.Error(w, "Invalid authorization header", http.StatusUnauthorized)
            return
        }
        
        claims, err := s.jwtManager.Verify(parts[1])
        if err != nil {
            http.Error(w, "Invalid token", http.StatusUnauthorized)
            return
        }
        
        // Add user info to context
        ctx := context.WithValue(r.Context(), "user_id", claims.UserID)
        ctx = context.WithValue(ctx, "username", claims.Username)
        
        next.ServeHTTP(w, r.WithContext(ctx))
    })
}

func hashPassword(password string) string {
    bytes, _ := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
    return string(bytes)
}

Session Management

Step 1: Session Store

// File: internal/session/store.go
package session

import (
    "crypto/rand"
    "database/sql"
    "encoding/base64"
    "errors"
    "net/http"
    "time"
)

type Session struct {
    ID        string    `json:"id"`
    UserID    int       `json:"user_id"`
    ExpiresAt time.Time `json:"expires_at"`
    CreatedAt time.Time `json:"created_at"`
}

type Store struct {
    db            *sql.DB
    cookieName    string
    cookiePath    string
    cookieDomain  string
    cookieSecure  bool
    cookieHTTPOnly bool
    sessionLength time.Duration
}

func NewStore(db *sql.DB, cookieName string, sessionLength time.Duration) *Store {
    return &Store{
        db:             db,
        cookieName:     cookieName,
        cookiePath:     "/",
        cookieHTTPOnly: true,
        sessionLength:  sessionLength,
    }
}

func (s *Store) generateID() (string, error) {
    b := make([]byte, 32)
    if _, err := rand.Read(b); err != nil {
        return "", err
    }
    return base64.URLEncoding.EncodeToString(b), nil
}

func (s *Store) Create(userID int) (*Session, error) {
    id, err := s.generateID()
    if err != nil {
        return nil, err
    }
    
    session := &Session{
        ID:        id,
        UserID:    userID,
        ExpiresAt: time.Now().Add(s.sessionLength),
        CreatedAt: time.Now(),
    }
    
    query := `
        INSERT INTO sessions (id, user_id, expires_at, created_at)
        VALUES ($1, $2, $3, $4)
    `
    
    _, err = s.db.Exec(query, session.ID, session.UserID, session.ExpiresAt, session.CreatedAt)
    if err != nil {
        return nil, err
    }
    
    return session, nil
}

func (s *Store) Get(id string) (*Session, error) {
    session := &Session{}
    query := `
        SELECT id, user_id, expires_at, created_at
        FROM sessions
        WHERE id = $1 AND expires_at > NOW()
    `
    
    err := s.db.QueryRow(query, id).Scan(
        &session.ID, &session.UserID, &session.ExpiresAt, &session.CreatedAt,
    )
    
    if err == sql.ErrNoRows {
        return nil, errors.New("session not found or expired")
    }
    
    return session, err
}

func (s *Store) Delete(id string) error {
    _, err := s.db.Exec("DELETE FROM sessions WHERE id = $1", id)
    return err
}

func (s *Store) Cleanup() error {
    _, err := s.db.Exec("DELETE FROM sessions WHERE expires_at < NOW()")
    return err
}

func (s *Store) SetCookie(w http.ResponseWriter, session *Session) {
    cookie := &http.Cookie{
        Name:     s.cookieName,
        Value:    session.ID,
        Path:     s.cookiePath,
        Domain:   s.cookieDomain,
        Expires:  session.ExpiresAt,
        Secure:   s.cookieSecure,
        HttpOnly: s.cookieHTTPOnly,
        SameSite: http.SameSiteLaxMode,
    }
    
    http.SetCookie(w, cookie)
}

func (s *Store) GetFromRequest(r *http.Request) (*Session, error) {
    cookie, err := r.Cookie(s.cookieName)
    if err != nil {
        return nil, err
    }
    
    return s.Get(cookie.Value)
}

func (s *Store) DeleteCookie(w http.ResponseWriter) {
    cookie := &http.Cookie{
        Name:     s.cookieName,
        Value:    "",
        Path:     s.cookiePath,
        Domain:   s.cookieDomain,
        MaxAge:   -1,
        Secure:   s.cookieSecure,
        HttpOnly: s.cookieHTTPOnly,
        SameSite: http.SameSiteLaxMode,
    }
    
    http.SetCookie(w, cookie)
}

WebSocket Implementation

Step 1: WebSocket Server

// File: internal/websocket/hub.go
package websocket

import (
    "log"
    "net/http"
    "github.com/gorilla/websocket"
)

var upgrader = websocket.Upgrader{
    ReadBufferSize:  1024,
    WriteBufferSize: 1024,
    CheckOrigin: func(r *http.Request) bool {
        // Configure origin checking for production
        return true
    },
}

type Message struct {
    Type    string      `json:"type"`
    UserID  int         `json:"user_id"`
    Content interface{} `json:"content"`
}

type Client struct {
    hub      *Hub
    conn     *websocket.Conn
    send     chan Message
    userID   int
    username string
}

type Hub struct {
    clients    map[*Client]bool
    broadcast  chan Message
    register   chan *Client
    unregister chan *Client
}

func NewHub() *Hub {
    return &Hub{
        broadcast:  make(chan Message),
        register:   make(chan *Client),
        unregister: make(chan *Client),
        clients:    make(map[*Client]bool),
    }
}

func (h *Hub) Run() {
    for {
        select {
        case client := <-h.register:
            h.clients[client] = true
            log.Printf("Client %s connected", client.username)
            
            // Notify other clients
            h.broadcast <- Message{
                Type:    "user_joined",
                UserID:  client.userID,
                Content: map[string]string{"username": client.username},
            }
            
        case client := <-h.unregister:
            if _, ok := h.clients[client]; ok {
                delete(h.clients, client)
                close(client.send)
                log.Printf("Client %s disconnected", client.username)
                
                // Notify other clients
                h.broadcast <- Message{
                    Type:    "user_left",
                    UserID:  client.userID,
                    Content: map[string]string{"username": client.username},
                }
            }
            
        case message := <-h.broadcast:
            for client := range h.clients {
                select {
                case client.send <- message:
                default:
                    close(client.send)
                    delete(h.clients, client)
                }
            }
        }
    }
}

func (c *Client) readPump() {
    defer func() {
        c.hub.unregister <- c
        c.conn.Close()
    }()
    
    for {
        var message Message
        err := c.conn.ReadJSON(&message)
        if err != nil {
            if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) {
                log.Printf("error: %v", err)
            }
            break
        }
        
        message.UserID = c.userID
        c.hub.broadcast <- message
    }
}

func (c *Client) writePump() {
    defer c.conn.Close()
    
    for {
        select {
        case message, ok := <-c.send:
            if !ok {
                c.conn.WriteMessage(websocket.CloseMessage, []byte{})
                return
            }
            
            c.conn.WriteJSON(message)
        }
    }
}

func ServeWS(hub *Hub, w http.ResponseWriter, r *http.Request) {
    // Get user from context (set by auth middleware)
    userID := r.Context().Value("user_id").(int)
    username := r.Context().Value("username").(string)
    
    conn, err := upgrader.Upgrade(w, r, nil)
    if err != nil {
        log.Println(err)
        return
    }
    
    client := &Client{
        hub:      hub,
        conn:     conn,
        send:     make(chan Message, 256),
        userID:   userID,
        username: username,
    }
    
    client.hub.register <- client
    
    go client.writePump()
    go client.readPump()
}

Microservices Architecture

Step 1: Service Structure

# Create microservice structure
cat > /usr/local/bin/create-microservice << 'EOF'
#!/bin/sh
# Create Go microservice

SERVICE_NAME=$1
if [ -z "$SERVICE_NAME" ]; then
    echo "Usage: create-microservice <service-name>"
    exit 1
fi

mkdir -p services/$SERVICE_NAME/{cmd,internal,api,configs,deployments}

# Service main.go
cat > services/$SERVICE_NAME/cmd/main.go << 'EOG'
package main

import (
    "context"
    "log"
    "net"
    "os"
    "os/signal"
    "syscall"

    "google.golang.org/grpc"
    "google.golang.org/grpc/health"
    "google.golang.org/grpc/health/grpc_health_v1"
)

func main() {
    port := os.Getenv("PORT")
    if port == "" {
        port = "50051"
    }

    lis, err := net.Listen("tcp", ":"+port)
    if err != nil {
        log.Fatalf("failed to listen: %v", err)
    }

    s := grpc.NewServer()
    
    // Register your service here
    // pb.RegisterYourServiceServer(s, &server{})
    
    // Health check
    grpc_health_v1.RegisterHealthServer(s, health.NewServer())
    
    go func() {
        log.Printf("gRPC server starting on port %s", port)
        if err := s.Serve(lis); err != nil {
            log.Fatalf("failed to serve: %v", err)
        }
    }()

    // Graceful shutdown
    sigChan := make(chan os.Signal, 1)
    signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
    <-sigChan

    log.Println("Shutting down gRPC server...")
    s.GracefulStop()
}
EOG

# Dockerfile
cat > services/$SERVICE_NAME/Dockerfile << 'EOD'
# Build stage
FROM golang:1.21-alpine AS builder

RUN apk add --no-cache git

WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download

COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main cmd/main.go

# Final stage
FROM alpine:latest

RUN apk --no-cache add ca-certificates
WORKDIR /root/

COPY --from=builder /app/main .

EXPOSE 50051
CMD ["./main"]
EOD

echo "Microservice $SERVICE_NAME created successfully!"
EOF

chmod +x /usr/local/bin/create-microservice

Step 2: API Gateway

// File: services/api-gateway/main.go
package main

import (
    "context"
    "encoding/json"
    "log"
    "net/http"
    "os"
    "time"

    "github.com/gorilla/mux"
    "google.golang.org/grpc"
    "google.golang.org/grpc/credentials/insecure"
)

type Gateway struct {
    userConn    *grpc.ClientConn
    productConn *grpc.ClientConn
    orderConn   *grpc.ClientConn
}

func NewGateway() (*Gateway, error) {
    // Connect to microservices
    userConn, err := grpc.Dial("user-service:50051", grpc.WithTransportCredentials(insecure.NewCredentials()))
    if err != nil {
        return nil, err
    }
    
    productConn, err := grpc.Dial("product-service:50052", grpc.WithTransportCredentials(insecure.NewCredentials()))
    if err != nil {
        return nil, err
    }
    
    orderConn, err := grpc.Dial("order-service:50053", grpc.WithTransportCredentials(insecure.NewCredentials()))
    if err != nil {
        return nil, err
    }
    
    return &Gateway{
        userConn:    userConn,
        productConn: productConn,
        orderConn:   orderConn,
    }, nil
}

func (g *Gateway) Close() {
    g.userConn.Close()
    g.productConn.Close()
    g.orderConn.Close()
}

func (g *Gateway) handleUsers(w http.ResponseWriter, r *http.Request) {
    // Proxy to user service
    // Implementation depends on your protobuf definitions
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(map[string]string{"service": "users"})
}

func (g *Gateway) handleProducts(w http.ResponseWriter, r *http.Request) {
    // Proxy to product service
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(map[string]string{"service": "products"})
}

func (g *Gateway) handleOrders(w http.ResponseWriter, r *http.Request) {
    // Proxy to order service
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(map[string]string{"service": "orders"})
}

func main() {
    gateway, err := NewGateway()
    if err != nil {
        log.Fatal(err)
    }
    defer gateway.Close()
    
    router := mux.NewRouter()
    
    // API routes
    api := router.PathPrefix("/api/v1").Subrouter()
    api.HandleFunc("/users", gateway.handleUsers)
    api.HandleFunc("/products", gateway.handleProducts)
    api.HandleFunc("/orders", gateway.handleOrders)
    
    // Health check
    router.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
        w.WriteHeader(http.StatusOK)
        json.NewEncoder(w).Encode(map[string]string{"status": "healthy"})
    })
    
    srv := &http.Server{
        Addr:         ":8080",
        Handler:      router,
        ReadTimeout:  15 * time.Second,
        WriteTimeout: 15 * time.Second,
        IdleTimeout:  60 * time.Second,
    }
    
    log.Println("API Gateway starting on :8080")
    if err := srv.ListenAndServe(); err != nil {
        log.Fatal(err)
    }
}

Testing and Benchmarking

Step 1: Unit Tests

// File: internal/repository/user_test.go
package repository

import (
    "database/sql"
    "testing"
    "time"

    "github.com/DATA-DOG/go-sqlmock"
    "github.com/stretchr/testify/assert"
)

func TestUserRepository_Create(t *testing.T) {
    db, mock, err := sqlmock.New()
    assert.NoError(t, err)
    defer db.Close()
    
    repo := NewUserRepository(db)
    
    user := &User{
        Username:     "testuser",
        Email:        "[email protected]",
        PasswordHash: "hash",
    }
    
    rows := sqlmock.NewRows([]string{"id", "created_at", "updated_at"}).
        AddRow(1, time.Now(), time.Now())
    
    mock.ExpectQuery("INSERT INTO users").
        WithArgs(user.Username, user.Email, user.PasswordHash).
        WillReturnRows(rows)
    
    err = repo.Create(user)
    assert.NoError(t, err)
    assert.Equal(t, 1, user.ID)
    
    err = mock.ExpectationsWereMet()
    assert.NoError(t, err)
}

func TestUserRepository_GetByID(t *testing.T) {
    db, mock, err := sqlmock.New()
    assert.NoError(t, err)
    defer db.Close()
    
    repo := NewUserRepository(db)
    
    expectedUser := &User{
        ID:           1,
        Username:     "testuser",
        Email:        "[email protected]",
        PasswordHash: "hash",
        CreatedAt:    time.Now(),
        UpdatedAt:    time.Now(),
    }
    
    rows := sqlmock.NewRows([]string{"id", "username", "email", "password_hash", "created_at", "updated_at"}).
        AddRow(expectedUser.ID, expectedUser.Username, expectedUser.Email, 
               expectedUser.PasswordHash, expectedUser.CreatedAt, expectedUser.UpdatedAt)
    
    mock.ExpectQuery("SELECT (.+) FROM users WHERE id").
        WithArgs(1).
        WillReturnRows(rows)
    
    user, err := repo.GetByID(1)
    assert.NoError(t, err)
    assert.Equal(t, expectedUser.Username, user.Username)
    
    err = mock.ExpectationsWereMet()
    assert.NoError(t, err)
}

Step 2: Integration Tests

// File: test/integration/api_test.go
package integration

import (
    "bytes"
    "encoding/json"
    "net/http"
    "net/http/httptest"
    "testing"

    "github.com/stretchr/testify/assert"
)

func TestAPI_CreateUser(t *testing.T) {
    // Setup test server
    server := setupTestServer()
    
    // Create user request
    payload := map[string]string{
        "username": "testuser",
        "email":    "[email protected]",
        "password": "password123",
    }
    
    body, _ := json.Marshal(payload)
    req := httptest.NewRequest("POST", "/api/v1/users", bytes.NewBuffer(body))
    req.Header.Set("Content-Type", "application/json")
    
    // Record response
    w := httptest.NewRecorder()
    server.router.ServeHTTP(w, req)
    
    // Assert
    assert.Equal(t, http.StatusCreated, w.Code)
    
    var response map[string]interface{}
    json.Unmarshal(w.Body.Bytes(), &response)
    
    assert.Equal(t, "testuser", response["username"])
    assert.Equal(t, "[email protected]", response["email"])
    assert.NotNil(t, response["id"])
}

func TestAPI_GetUser(t *testing.T) {
    server := setupTestServer()
    
    // Create user first
    user := createTestUser(t, server)
    
    // Get user
    req := httptest.NewRequest("GET", "/api/v1/users/"+user.ID, nil)
    w := httptest.NewRecorder()
    server.router.ServeHTTP(w, req)
    
    assert.Equal(t, http.StatusOK, w.Code)
    
    var response map[string]interface{}
    json.Unmarshal(w.Body.Bytes(), &response)
    
    assert.Equal(t, user.Username, response["username"])
}

Step 3: Benchmarks

// File: internal/repository/user_bench_test.go
package repository

import (
    "testing"
)

func BenchmarkUserRepository_Create(b *testing.B) {
    db := setupTestDB()
    defer db.Close()
    
    repo := NewUserRepository(db)
    
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        user := &User{
            Username:     fmt.Sprintf("user%d", i),
            Email:        fmt.Sprintf("user%d@example.com", i),
            PasswordHash: "hash",
        }
        repo.Create(user)
    }
}

func BenchmarkUserRepository_GetByID(b *testing.B) {
    db := setupTestDB()
    defer db.Close()
    
    repo := NewUserRepository(db)
    
    // Create test user
    user := &User{
        Username:     "benchuser",
        Email:        "[email protected]",
        PasswordHash: "hash",
    }
    repo.Create(user)
    
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        repo.GetByID(user.ID)
    }
}

// Run benchmarks with:
// go test -bench=. -benchmem ./...

Deployment Strategies

Step 1: Docker Deployment

# File: Dockerfile
# Build stage
FROM golang:1.21-alpine AS builder

# Install dependencies
RUN apk add --no-cache git ca-certificates

# Set working directory
WORKDIR /app

# Copy go mod files
COPY go.mod go.sum ./
RUN go mod download

# Copy source code
COPY . .

# Build application
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main cmd/main.go

# Final stage
FROM alpine:latest

# Install ca-certificates for HTTPS
RUN apk --no-cache add ca-certificates

WORKDIR /root/

# Copy binary from builder
COPY --from=builder /app/main .
COPY --from=builder /app/web ./web
COPY --from=builder /app/configs ./configs

# Expose port
EXPOSE 8080

# Run application
CMD ["./main"]

Step 2: Docker Compose

# File: docker-compose.yml
version: '3.8'

services:
  app:
    build: .
    ports:
      - "8080:8080"
    environment:
      - PORT=8080
      - DB_HOST=postgres
      - DB_PORT=5432
      - DB_USER=appuser
      - DB_PASSWORD=apppassword
      - DB_NAME=appdb
      - REDIS_URL=redis://redis:6379
    depends_on:
      - postgres
      - redis
    networks:
      - app-network

  postgres:
    image: postgres:15-alpine
    environment:
      - POSTGRES_USER=appuser
      - POSTGRES_PASSWORD=apppassword
      - POSTGRES_DB=appdb
    ports:
      - "5432:5432"
    volumes:
      - postgres-data:/var/lib/postgresql/data
    networks:
      - app-network

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    volumes:
      - redis-data:/data
    networks:
      - app-network

  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
      - ./certs:/etc/nginx/certs
    depends_on:
      - app
    networks:
      - app-network

volumes:
  postgres-data:
  redis-data:

networks:
  app-network:
    driver: bridge

Step 3: Kubernetes Deployment

# File: k8s/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: go-app
  labels:
    app: go-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: go-app
  template:
    metadata:
      labels:
        app: go-app
    spec:
      containers:
      - name: go-app
        image: your-registry/go-app:latest
        ports:
        - containerPort: 8080
        env:
        - name: PORT
          value: "8080"
        - name: DB_HOST
          valueFrom:
            secretKeyRef:
              name: db-secret
              key: host
        resources:
          requests:
            memory: "128Mi"
            cpu: "100m"
          limits:
            memory: "256Mi"
            cpu: "200m"
        livenessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 5

---
apiVersion: v1
kind: Service
metadata:
  name: go-app-service
spec:
  selector:
    app: go-app
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080
  type: LoadBalancer

Performance Optimization

Step 1: Profiling

// File: cmd/main.go - Add profiling endpoints
import (
    _ "net/http/pprof"
)

func main() {
    // Enable profiling in development
    if os.Getenv("ENABLE_PROFILING") == "true" {
        go func() {
            log.Println("Starting profiling server on :6060")
            log.Println(http.ListenAndServe(":6060", nil))
        }()
    }
    
    // Your application code...
}

// Profile CPU usage:
// go tool pprof http://localhost:6060/debug/pprof/profile?seconds=30
// Profile memory:
// go tool pprof http://localhost:6060/debug/pprof/heap

Step 2: Caching

// File: internal/cache/redis.go
package cache

import (
    "context"
    "encoding/json"
    "time"

    "github.com/go-redis/redis/v8"
)

type Cache struct {
    client *redis.Client
}

func NewCache(addr string) *Cache {
    client := redis.NewClient(&redis.Options{
        Addr:     addr,
        Password: "",
        DB:       0,
    })
    
    return &Cache{client: client}
}

func (c *Cache) Set(ctx context.Context, key string, value interface{}, expiration time.Duration) error {
    json, err := json.Marshal(value)
    if err != nil {
        return err
    }
    
    return c.client.Set(ctx, key, json, expiration).Err()
}

func (c *Cache) Get(ctx context.Context, key string, dest interface{}) error {
    val, err := c.client.Get(ctx, key).Result()
    if err != nil {
        return err
    }
    
    return json.Unmarshal([]byte(val), dest)
}

func (c *Cache) Delete(ctx context.Context, key string) error {
    return c.client.Del(ctx, key).Err()
}

// Middleware for caching
func CacheMiddleware(cache *Cache, duration time.Duration) func(http.Handler) http.Handler {
    return func(next http.Handler) http.Handler {
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            if r.Method != "GET" {
                next.ServeHTTP(w, r)
                return
            }
            
            key := r.URL.Path
            
            // Try to get from cache
            var cachedResponse []byte
            if err := cache.Get(r.Context(), key, &cachedResponse); err == nil {
                w.Header().Set("X-Cache", "HIT")
                w.Write(cachedResponse)
                return
            }
            
            // Record response
            rec := &responseRecorder{ResponseWriter: w, body: &bytes.Buffer{}}
            next.ServeHTTP(rec, r)
            
            // Cache successful responses
            if rec.status == http.StatusOK {
                cache.Set(r.Context(), key, rec.body.Bytes(), duration)
            }
            
            w.Header().Set("X-Cache", "MISS")
        })
    }
}

Monitoring and Logging

Step 1: Structured Logging

// File: internal/logger/logger.go
package logger

import (
    "os"

    "go.uber.org/zap"
    "go.uber.org/zap/zapcore"
)

func NewLogger(env string) (*zap.Logger, error) {
    var config zap.Config
    
    if env == "production" {
        config = zap.NewProductionConfig()
        config.EncoderConfig.TimeKey = "timestamp"
        config.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
    } else {
        config = zap.NewDevelopmentConfig()
        config.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder
    }
    
    config.OutputPaths = []string{"stdout"}
    config.ErrorOutputPaths = []string{"stderr"}
    
    return config.Build()
}

// Middleware for request logging
func LoggingMiddleware(logger *zap.Logger) func(http.Handler) http.Handler {
    return func(next http.Handler) http.Handler {
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            start := time.Now()
            
            wrapped := &responseWriter{
                ResponseWriter: w,
                status:        http.StatusOK,
            }
            
            next.ServeHTTP(wrapped, r)
            
            logger.Info("request",
                zap.String("method", r.Method),
                zap.String("path", r.URL.Path),
                zap.Int("status", wrapped.status),
                zap.Duration("duration", time.Since(start)),
                zap.String("user_agent", r.UserAgent()),
                zap.String("remote_addr", r.RemoteAddr),
            )
        })
    }
}

Step 2: Metrics with Prometheus

// File: internal/metrics/metrics.go
package metrics

import (
    "net/http"

    "github.com/prometheus/client_golang/prometheus"
    "github.com/prometheus/client_golang/prometheus/promhttp"
)

type Metrics struct {
    requestsTotal   *prometheus.CounterVec
    requestDuration *prometheus.HistogramVec
    activeRequests  prometheus.Gauge
}

func NewMetrics() *Metrics {
    m := &Metrics{
        requestsTotal: prometheus.NewCounterVec(
            prometheus.CounterOpts{
                Name: "http_requests_total",
                Help: "Total number of HTTP requests",
            },
            []string{"method", "endpoint", "status"},
        ),
        requestDuration: prometheus.NewHistogramVec(
            prometheus.HistogramOpts{
                Name:    "http_request_duration_seconds",
                Help:    "HTTP request duration in seconds",
                Buckets: prometheus.DefBuckets,
            },
            []string{"method", "endpoint"},
        ),
        activeRequests: prometheus.NewGauge(
            prometheus.GaugeOpts{
                Name: "http_requests_active",
                Help: "Number of active HTTP requests",
            },
        ),
    }
    
    prometheus.MustRegister(m.requestsTotal)
    prometheus.MustRegister(m.requestDuration)
    prometheus.MustRegister(m.activeRequests)
    
    return m
}

func (m *Metrics) Middleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        start := time.Now()
        
        m.activeRequests.Inc()
        defer m.activeRequests.Dec()
        
        wrapped := &responseWriter{
            ResponseWriter: w,
            status:        http.StatusOK,
        }
        
        next.ServeHTTP(wrapped, r)
        
        duration := time.Since(start).Seconds()
        
        m.requestsTotal.WithLabelValues(
            r.Method,
            r.URL.Path,
            http.StatusText(wrapped.status),
        ).Inc()
        
        m.requestDuration.WithLabelValues(
            r.Method,
            r.URL.Path,
        ).Observe(duration)
    })
}

func (m *Metrics) Handler() http.Handler {
    return promhttp.Handler()
}

Troubleshooting

Common Issues

  1. Module dependencies issues:
# Clear module cache
go clean -modcache

# Download dependencies
go mod download

# Verify dependencies
go mod verify

# Update dependencies
go get -u ./...
  1. Build failures on Alpine:
# Install build dependencies
apk add gcc musl-dev

# For CGO dependencies
CGO_ENABLED=1 go build

# For static binary
CGO_ENABLED=0 go build -ldflags="-w -s"
  1. Database connection issues:
# Check PostgreSQL connection
psql -h localhost -U user -d dbname

# Test with Go
go run -tags=debug main.go

Debug Configuration

// File: internal/debug/debug.go
// +build debug

package debug

import (
    "log"
    "net/http"
    "runtime"
)

func init() {
    log.Println("DEBUG MODE ENABLED")
    
    // Enable verbose logging
    log.SetFlags(log.LstdFlags | log.Lshortfile)
    
    // Memory stats endpoint
    http.HandleFunc("/debug/memstats", func(w http.ResponseWriter, r *http.Request) {
        var m runtime.MemStats
        runtime.ReadMemStats(&m)
        
        fmt.Fprintf(w, "Alloc = %v MB\n", m.Alloc/1024/1024)
        fmt.Fprintf(w, "TotalAlloc = %v MB\n", m.TotalAlloc/1024/1024)
        fmt.Fprintf(w, "Sys = %v MB\n", m.Sys/1024/1024)
        fmt.Fprintf(w, "NumGC = %v\n", m.NumGC)
    })
}

Best Practices

Project Structure

# Recommended project structure
cat > PROJECT_STRUCTURE.md << 'EOF'
# Go Project Structure

. โ”œโ”€โ”€ cmd/ # Application entrypoints โ”‚ โ”œโ”€โ”€ api/ # API server โ”‚ โ”œโ”€โ”€ worker/ # Background workers โ”‚ โ””โ”€โ”€ migrate/ # Database migrations โ”œโ”€โ”€ internal/ # Private application code โ”‚ โ”œโ”€โ”€ api/ # API handlers โ”‚ โ”œโ”€โ”€ auth/ # Authentication โ”‚ โ”œโ”€โ”€ config/ # Configuration โ”‚ โ”œโ”€โ”€ database/ # Database connections โ”‚ โ”œโ”€โ”€ middleware/ # HTTP middleware โ”‚ โ”œโ”€โ”€ models/ # Data models โ”‚ โ”œโ”€โ”€ repository/ # Data access layer โ”‚ โ”œโ”€โ”€ service/ # Business logic โ”‚ โ””โ”€โ”€ validator/ # Input validation โ”œโ”€โ”€ pkg/ # Public packages โ”œโ”€โ”€ web/ # Web assets โ”‚ โ”œโ”€โ”€ static/ # CSS, JS, images โ”‚ โ””โ”€โ”€ templates/ # HTML templates โ”œโ”€โ”€ configs/ # Configuration files โ”œโ”€โ”€ deployments/ # Deployment configurations โ”œโ”€โ”€ docs/ # Documentation โ”œโ”€โ”€ scripts/ # Build and deploy scripts โ”œโ”€โ”€ test/ # Additional test files โ”œโ”€โ”€ .gitignore โ”œโ”€โ”€ .golangci.yml # Linter configuration โ”œโ”€โ”€ Dockerfile โ”œโ”€โ”€ Makefile โ”œโ”€โ”€ README.md โ”œโ”€โ”€ go.mod โ””โ”€โ”€ go.sum

EOF

Code Quality

# File: .golangci.yml
linters:
  enable:
    - gofmt
    - golint
    - govet
    - errcheck
    - staticcheck
    - gosimple
    - structcheck
    - varcheck
    - ineffassign
    - deadcode
    - typecheck
    - gosec
    - gocyclo
    - dupl
    - misspell
    - unparam
    - nakedret
    - prealloc
    - scopelint
    - gocritic
    - gochecknoinits
    - gochecknoglobals

linters-settings:
  gocyclo:
    min-complexity: 15
  dupl:
    threshold: 100
  goconst:
    min-len: 2
    min-occurrences: 2

run:
  skip-dirs:
    - vendor
    - test
  skip-files:
    - ".*_test.go"

issues:
  exclude-rules:
    - path: _test\.go
      linters:
        - gosec
        - dupl

Makefile

# File: Makefile
.PHONY: build run test clean docker-build docker-run lint

# Variables
BINARY_NAME=app
DOCKER_IMAGE=go-app:latest

# Build
build:
	go build -ldflags="-w -s" -o bin/$(BINARY_NAME) cmd/main.go

# Run
run:
	go run cmd/main.go

# Test
test:
	go test -v -race -coverprofile=coverage.out ./...
	go tool cover -html=coverage.out -o coverage.html

# Benchmark
bench:
	go test -bench=. -benchmem ./...

# Lint
lint:
	golangci-lint run

# Clean
clean:
	go clean
	rm -rf bin/
	rm -f coverage.out coverage.html

# Docker
docker-build:
	docker build -t $(DOCKER_IMAGE) .

docker-run:
	docker run -p 8080:8080 $(DOCKER_IMAGE)

# Database migrations
migrate-up:
	migrate -path ./migrations -database "postgresql://user:pass@localhost/db?sslmode=disable" up

migrate-down:
	migrate -path ./migrations -database "postgresql://user:pass@localhost/db?sslmode=disable" down

# Development with hot reload
dev:
	air

# Generate
generate:
	go generate ./...

Conclusion

Youโ€™ve successfully set up a comprehensive Go web application development environment on Alpine Linux. This setup includes everything from basic web servers to microservices architectures, with proper testing, deployment strategies, and monitoring. Alpine Linuxโ€™s minimal footprint combined with Goโ€™s efficiency creates an excellent platform for building high-performance web applications.

Remember to keep your dependencies updated, follow Go best practices, and leverage Alpineโ€™s security features. With this foundation, youโ€™re ready to build scalable, maintainable Go applications that can handle production workloads efficiently.