+
+
+
influxdb
echo
+
swc
numpy
scheme
+
tcl
∫
git
+
+
parcel
android
gentoo
delphi
+
julia
+
+
+
jwt
+
k8s
+
sublime
crystal
wasm
+
+
centos
<-
+
abap
xgboost
k8s
yarn
+
fiber
+
django
strapi
+
+
+
aws
$
+
+
+
numpy
+
+
+
+
protobuf
toml
+
nvim
+
+
+
azure
+
yaml
+
%
!==
+
+
++
next
+
+
+
+
phpstorm
elixir
ubuntu
gatsby
&
+
spacy
+
+
Back to Blog
Setting Up Go Web Applications on Alpine Linux πŸš€
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.