๐น Go Application Deployment on AlmaLinux: Complete Production Guide
Welcome to the lightning-fast world of Go deployment! โก Today weโll master deploying Go applications on AlmaLinux, harnessing the incredible speed and efficiency that has made Go the language of choice for modern backend systems. Think of Go as the Formula 1 race car of programming languages - built for speed, reliability, and performance! ๐๏ธ
From Googleโs infrastructure to Docker, Kubernetes, and countless startups, Go powers the backbone of modern internet services. By deploying Go applications on AlmaLinux, youโre combining Googleโs performance-optimized language with enterprise-grade Linux stability. Letโs build something amazing! ๐ช
๐ค Why is Go Application Deployment Important?
Go deployment is revolutionizing modern backend development! ๐ Hereโs why Go on AlmaLinux dominates:
- โก Blazing Performance - Go applications start in milliseconds and handle millions of requests
- ๐ฅ Minimal Resource Usage - Lower memory footprint than Java, Python, or Node.js
- ๐ฆ Single Binary Deployment - No dependencies, just copy and run
- ๐ Built for Concurrency - Handle thousands of simultaneous connections
- ๐ ๏ธ Simple Operations - No complex runtime environments or virtual machines
- ๐ฐ Cost Effective - Fewer servers needed due to efficiency
- ๐ง Developer Productivity - Fast compilation and excellent tooling
- ๐ข Enterprise Ready - Used by Google, Uber, Netflix, and thousands of companies
๐ฏ What You Need
Before deploying Go applications like a pro, ensure you have:
โ
AlmaLinux system (physical or virtual machine)
โ
Root or sudo access for installing packages
โ
Basic programming knowledge (any language is fine)
โ
Text editor like nano, vim, or VS Code
โ
Internet connection for downloading Go and packages
โ
At least 1GB RAM (Go is very efficient!)
โ
10GB free disk space for Go toolchain and projects
โ
Basic command line skills (weโll guide you through everything!)
๐ Installing Go on AlmaLinux
Letโs install the latest Go toolchain! ๐ ๏ธ
# Update your system first
sudo dnf update -y
# Ensures you have the latest packages
# Install development tools
sudo dnf groupinstall -y "Development Tools"
sudo dnf install -y wget curl git vim
# Essential tools for Go development
# Download the latest Go binary
GO_VERSION="1.21.4" # Check golang.org for latest version
cd /tmp
wget https://go.dev/dl/go${GO_VERSION}.linux-amd64.tar.gz
# Downloads Go binary distribution
# Verify the download (optional but recommended)
sha256sum go${GO_VERSION}.linux-amd64.tar.gz
# Compare with checksum from golang.org
# Remove any existing Go installation
sudo rm -rf /usr/local/go
# Cleans up old installations
# Install Go to /usr/local
sudo tar -xzf go${GO_VERSION}.linux-amd64.tar.gz -C /usr/local
# Extracts Go to the standard location
# Set up environment variables
echo 'export PATH=$PATH:/usr/local/go/bin' >> ~/.bashrc
echo 'export GOPATH=$HOME/go' >> ~/.bashrc
echo 'export GOROOT=/usr/local/go' >> ~/.bashrc
# Configures Go environment
# Reload your shell configuration
source ~/.bashrc
# Create Go workspace directories
mkdir -p $HOME/go/{bin,src,pkg}
# Creates standard Go workspace structure
# Verify Go installation
go version
# Should show: go version go1.21.4 linux/amd64
go env GOPATH
# Should show: /home/youruser/go
๐ง Creating Your First Go Web Application
Letโs build a production-ready Go web API! ๐๏ธ
# Create a new project directory
mkdir -p $HOME/go/src/myapi
cd $HOME/go/src/myapi
# Initialize Go module
go mod init myapi
# Creates go.mod file for dependency management
# Install popular web framework (Gin)
go get github.com/gin-gonic/gin
go get github.com/gin-contrib/cors
# Installs Gin web framework and CORS middleware
# Create main application file
cat > main.go << 'EOF'
package main
import (
"fmt"
"log"
"net/http"
"os"
"time"
"github.com/gin-contrib/cors"
"github.com/gin-gonic/gin"
)
type HealthResponse struct {
Status string `json:"status"`
Timestamp time.Time `json:"timestamp"`
Version string `json:"version"`
Host string `json:"host"`
}
type UserRequest struct {
Name string `json:"name" binding:"required"`
Email string `json:"email" binding:"required,email"`
}
type UserResponse struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
CreatedAt time.Time `json:"created_at"`
}
func main() {
// Set Gin mode from environment variable
if os.Getenv("GIN_MODE") == "" {
gin.SetMode(gin.ReleaseMode)
}
// Create Gin router
r := gin.New()
// Add middleware
r.Use(gin.Logger())
r.Use(gin.Recovery())
// Configure CORS
config := cors.DefaultConfig()
config.AllowAllOrigins = true
config.AllowMethods = []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"}
config.AllowHeaders = []string{"Origin", "Content-Type", "Authorization"}
r.Use(cors.New(config))
// Health check endpoint
r.GET("/health", func(c *gin.Context) {
hostname, _ := os.Hostname()
c.JSON(http.StatusOK, HealthResponse{
Status: "healthy",
Timestamp: time.Now(),
Version: "1.0.0",
Host: hostname,
})
})
// API routes
api := r.Group("/api/v1")
{
api.GET("/hello", func(c *gin.Context) {
name := c.DefaultQuery("name", "World")
c.JSON(http.StatusOK, gin.H{
"message": fmt.Sprintf("Hello %s from Go on AlmaLinux! ๐น", name),
"timestamp": time.Now(),
"server": "Go/AlmaLinux",
})
})
api.POST("/users", func(c *gin.Context) {
var user UserRequest
if err := c.ShouldBindJSON(&user); err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"error": "Invalid request format",
"details": err.Error(),
})
return
}
// Simulate user creation (in real app, save to database)
response := UserResponse{
ID: 12345,
Name: user.Name,
Email: user.Email,
CreatedAt: time.Now(),
}
c.JSON(http.StatusCreated, response)
})
api.GET("/stats", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"requests_handled": 1000,
"uptime_seconds": time.Since(time.Now().Add(-time.Hour)).Seconds(),
"memory_usage": "12MB",
"goroutines": 10,
"go_version": fmt.Sprintf("%s", os.Getenv("GO_VERSION")),
})
})
}
// Get port from environment variable or default to 8080
port := os.Getenv("PORT")
if port == "" {
port = "8080"
}
log.Printf("๐ Starting Go API server on port %s", port)
log.Printf("๐ Health check: http://localhost:%s/health", port)
log.Printf("๐ API docs: http://localhost:%s/api/v1/hello", port)
// Start server
if err := r.Run(":" + port); err != nil {
log.Fatal("Failed to start server:", err)
}
}
EOF
# Creates a complete REST API with health checks
# Build the application
go build -o myapi
# Compiles Go code to binary executable
# Test the application
./myapi &
# Starts the server in background
# Test API endpoints
curl http://localhost:8080/health
# Should return health status JSON
curl "http://localhost:8080/api/v1/hello?name=Developer"
# Should return personalized greeting
curl -X POST http://localhost:8080/api/v1/users \
-H "Content-Type: application/json" \
-d '{"name":"John Doe","email":"[email protected]"}'
# Should create a user and return response
# Stop the test server
pkill myapi
๐ Production Build and Optimization
Letโs optimize our Go application for production deployment! โก
# Create optimized production build
go build -ldflags="-s -w" -o myapi-prod
# -s removes symbol table, -w removes debug info
# Check binary size
ls -lh myapi*
# Shows file sizes - Go binaries are impressively small!
# Cross-compile for different platforms (optional)
GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o myapi-linux-amd64
GOOS=darwin GOARCH=amd64 go build -ldflags="-s -w" -o myapi-darwin-amd64
# Creates binaries for different operating systems
# Create production configuration
cat > config.json << 'EOF'
{
"server": {
"port": "8080",
"host": "0.0.0.0",
"read_timeout": "30s",
"write_timeout": "30s",
"max_header_bytes": 8192
},
"database": {
"host": "localhost",
"port": "5432",
"name": "myapi",
"user": "apiuser",
"ssl_mode": "require"
},
"logging": {
"level": "info",
"format": "json",
"output": "/var/log/myapi/app.log"
}
}
EOF
# Configuration for production settings
# Create systemd-compatible version with configuration
cat > main-prod.go << 'EOF'
package main
import (
"context"
"encoding/json"
"fmt"
"log"
"net/http"
"os"
"os/signal"
"syscall"
"time"
"github.com/gin-contrib/cors"
"github.com/gin-gonic/gin"
)
type Config struct {
Server struct {
Port string `json:"port"`
Host string `json:"host"`
ReadTimeout string `json:"read_timeout"`
WriteTimeout string `json:"write_timeout"`
MaxHeaderBytes int `json:"max_header_bytes"`
} `json:"server"`
Logging struct {
Level string `json:"level"`
Format string `json:"format"`
Output string `json:"output"`
} `json:"logging"`
}
func loadConfig(path string) (*Config, error) {
config := &Config{}
file, err := os.Open(path)
if err != nil {
return nil, err
}
defer file.Close()
decoder := json.NewDecoder(file)
if err := decoder.Decode(config); err != nil {
return nil, err
}
return config, nil
}
func main() {
// Load configuration
config, err := loadConfig("config.json")
if err != nil {
log.Fatal("Failed to load config:", err)
}
gin.SetMode(gin.ReleaseMode)
r := gin.New()
// Middleware
r.Use(gin.Logger())
r.Use(gin.Recovery())
r.Use(cors.New(cors.DefaultConfig()))
// Routes
r.GET("/health", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"status": "healthy",
"timestamp": time.Now(),
"version": "1.0.0",
})
})
r.GET("/api/v1/hello", func(c *gin.Context) {
name := c.DefaultQuery("name", "World")
c.JSON(http.StatusOK, gin.H{
"message": fmt.Sprintf("Hello %s from Production Go API! ๐", name),
"timestamp": time.Now(),
})
})
// Create HTTP server with timeouts
srv := &http.Server{
Addr: config.Server.Host + ":" + config.Server.Port,
Handler: r,
ReadTimeout: 30 * time.Second,
WriteTimeout: 30 * time.Second,
MaxHeaderBytes: config.Server.MaxHeaderBytes,
}
// Start server in a goroutine
go func() {
log.Printf("๐ Production server starting on %s", srv.Addr)
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatal("Server failed to start:", err)
}
}()
// Wait for interrupt signal to gracefully shutdown
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
log.Println("๐ Shutting down server...")
// Give outstanding requests 5 seconds to complete
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := srv.Shutdown(ctx); err != nil {
log.Fatal("Server forced to shutdown:", err)
}
log.Println("โ
Server gracefully stopped")
}
EOF
# Build production version
go build -ldflags="-s -w" -o myapi-prod main-prod.go
โ Creating Systemd Service
Letโs make our Go application run as a system service! ๐ฏ
# Create application directory
sudo mkdir -p /opt/myapi
sudo mkdir -p /var/log/myapi
sudo mkdir -p /etc/myapi
# Copy application files
sudo cp myapi-prod /opt/myapi/myapi
sudo cp config.json /etc/myapi/
sudo chmod +x /opt/myapi/myapi
# Create dedicated user for the application
sudo useradd -r -s /bin/false -d /opt/myapi myapi
sudo chown -R myapi:myapi /opt/myapi /var/log/myapi /etc/myapi
# Creates system user for security
# Create systemd service file
sudo cat > /etc/systemd/system/myapi.service << 'EOF'
[Unit]
Description=Go API Service
Documentation=https://github.com/yourcompany/myapi
After=network.target
Wants=network-online.target
[Service]
Type=simple
User=myapi
Group=myapi
WorkingDirectory=/opt/myapi
ExecStart=/opt/myapi/myapi
ExecReload=/bin/kill -HUP $MAINPID
# Restart policy
Restart=always
RestartSec=5
# Resource limits
LimitNOFILE=100000
LimitNPROC=4096
# Security settings
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/var/log/myapi
# Environment variables
Environment="PORT=8080"
Environment="GIN_MODE=release"
Environment="CONFIG_PATH=/etc/myapi/config.json"
# Logging
StandardOutput=journal
StandardError=journal
SyslogIdentifier=myapi
[Install]
WantedBy=multi-user.target
EOF
# Creates production-ready systemd service
# Reload systemd and start service
sudo systemctl daemon-reload
sudo systemctl enable myapi
sudo systemctl start myapi
# Check service status
sudo systemctl status myapi
# Should show "active (running)"
# View logs
sudo journalctl -u myapi -f --no-pager
# Shows real-time application logs
# Test the service
curl http://localhost:8080/health
# Should return health status from systemd service
๐ฎ Quick Examples
Letโs explore practical Go deployment scenarios! ๐ฏ
Example 1: Nginx Reverse Proxy
# Install Nginx
sudo dnf install -y nginx
# Configure Nginx for Go application
sudo cat > /etc/nginx/conf.d/myapi.conf << 'EOF'
upstream go_backend {
server localhost:8080;
# Add more servers for load balancing
# server localhost:8081;
# server localhost:8082;
}
server {
listen 80;
server_name your-domain.com;
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
# Gzip compression
gzip on;
gzip_types text/plain application/json application/javascript text/css;
# API proxy
location /api {
proxy_pass http://go_backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
# Timeouts
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
# Health check
location /health {
proxy_pass http://go_backend/health;
access_log off;
}
# Static files (if any)
location /static/ {
alias /opt/myapi/static/;
expires 1y;
add_header Cache-Control "public, immutable";
}
}
EOF
# Test Nginx configuration
sudo nginx -t
# Start Nginx
sudo systemctl enable nginx
sudo systemctl start nginx
Example 2: Docker Deployment
# Create multi-stage Dockerfile
cat > Dockerfile << 'EOF'
# Build stage
FROM golang:1.21-alpine AS builder
# Install build dependencies
RUN apk add --no-cache git ca-certificates
# Set working directory
WORKDIR /app
# Copy go mod files
COPY go.mod go.sum ./
# Download dependencies
RUN go mod download
# Copy source code
COPY . .
# Build the application
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o main .
# Final stage
FROM alpine:latest
# Install ca-certificates for HTTPS requests
RUN apk --no-cache add ca-certificates
# Create non-root user
RUN adduser -D -s /bin/sh apiuser
# Set working directory
WORKDIR /app
# Copy binary from builder stage
COPY --from=builder /app/main .
COPY --from=builder /app/config.json .
# Change ownership
RUN chown -R apiuser:apiuser /app
# Switch to non-root user
USER apiuser
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1
# Expose port
EXPOSE 8080
# Run the application
CMD ["./main"]
EOF
# Create .dockerignore
cat > .dockerignore << 'EOF'
.git
.gitignore
README.md
Dockerfile
.dockerignore
myapi
myapi-prod
myapi-linux-amd64
myapi-darwin-amd64
EOF
# Build Docker image
docker build -t myapi:latest .
# Run container
docker run -d \
--name myapi-container \
--restart unless-stopped \
-p 8080:8080 \
-e GIN_MODE=release \
myapi:latest
# Check container logs
docker logs myapi-container
# Test containerized application
curl http://localhost:8080/health
Example 3: Load Testing and Monitoring
# Install hey (HTTP load testing tool)
go install github.com/rakyll/hey@latest
# Simple load test
hey -n 1000 -c 10 http://localhost:8080/health
# Sends 1000 requests with 10 concurrent connections
# More comprehensive test
hey -n 10000 -c 100 -t 30 http://localhost:8080/api/v1/hello
# 10,000 requests, 100 concurrent, 30-second timeout
# Create monitoring script
cat > monitor.sh << 'EOF'
#!/bin/bash
# Simple Go application monitoring script
API_URL="http://localhost:8080"
LOG_FILE="/var/log/myapi/monitor.log"
# Function to log messages
log_message() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$LOG_FILE"
}
# Check if application is responding
check_health() {
HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" "$API_URL/health")
if [ "$HTTP_STATUS" = "200" ]; then
log_message "โ
Application healthy (HTTP $HTTP_STATUS)"
return 0
else
log_message "โ Application unhealthy (HTTP $HTTP_STATUS)"
return 1
fi
}
# Check response time
check_response_time() {
RESPONSE_TIME=$(curl -s -o /dev/null -w "%{time_total}" "$API_URL/health")
RESPONSE_TIME_MS=$(echo "$RESPONSE_TIME * 1000" | bc)
log_message "โฑ๏ธ Response time: ${RESPONSE_TIME_MS}ms"
# Alert if response time > 1000ms
if (( $(echo "$RESPONSE_TIME > 1.0" | bc -l) )); then
log_message "โ ๏ธ High response time detected!"
fi
}
# Main monitoring loop
main() {
log_message "๐ Starting Go application monitoring"
while true; do
if check_health; then
check_response_time
else
# Try to restart service if unhealthy
log_message "๐ Attempting to restart myapi service"
systemctl restart myapi
sleep 10
fi
sleep 60 # Check every minute
done
}
main
EOF
chmod +x monitor.sh
# Run monitoring in background
nohup ./monitor.sh &
๐จ Fix Common Problems
Encountering issues? Here are solutions:
Problem 1: Binary Wonโt Run
# Check if binary has execute permissions
ls -la myapi
# Should show -rwxr-xr-x
# Fix permissions if needed
chmod +x myapi
# Check for missing libraries (shouldn't be an issue with Go)
ldd myapi
# Go binaries are statically linked
# Verify the binary is for correct architecture
file myapi
# Should show: ELF 64-bit LSB executable, x86-64
# Run with verbose output to see errors
./myapi -v
Problem 2: Service Wonโt Start
# Check systemd service status
sudo systemctl status myapi --no-pager -l
# Shows detailed error messages
# Check service logs
sudo journalctl -u myapi -n 50 --no-pager
# Shows last 50 log entries
# Verify file permissions
sudo ls -la /opt/myapi/
sudo ls -la /var/log/myapi/
# Test binary manually as service user
sudo -u myapi /opt/myapi/myapi
# Runs as the service user to test permissions
# Check if port is already in use
sudo netstat -tlnp | grep :8080
# Shows what's using port 8080
Problem 3: High Memory Usage
# Check Go application memory usage
ps aux | grep myapi
# Shows memory usage in RSS column
# Enable Go runtime memory stats
cat >> main.go << 'EOF'
import (
"runtime"
_ "net/http/pprof" // Add pprof endpoint
)
// In your main function, add:
go func() {
log.Println(http.ListenAndServe("localhost:6060", nil))
}()
EOF
# Rebuild and restart
go build -o myapi
sudo systemctl restart myapi
# Check memory profile
go tool pprof http://localhost:6060/debug/pprof/heap
# Opens interactive profiler
# Force garbage collection
curl http://localhost:6060/debug/pprof/gc
# Manually triggers GC
Problem 4: Performance Issues
# Enable profiling in production
export GODEBUG=gctrace=1
# Shows GC timing information
# Check goroutine usage
curl http://localhost:6060/debug/pprof/goroutine?debug=1
# Shows active goroutines
# CPU profiling
go tool pprof http://localhost:6060/debug/pprof/profile?seconds=30
# Profiles CPU usage for 30 seconds
# Optimize build with profile-guided optimization (PGO)
go build -pgo=cpu.prof -o myapi-optimized
# Uses profile data to optimize binary
# Monitor system resources
htop
iotop # Disk I/O
nethogs # Network usage
Problem 5: Connection Issues
# Check if service is listening on correct port
sudo netstat -tlnp | grep myapi
# Should show listening on port 8080
# Test local connection
telnet localhost 8080
# Should connect successfully
# Check firewall rules
sudo firewall-cmd --list-all
# Shows current firewall configuration
# Open port in firewall if needed
sudo firewall-cmd --permanent --add-port=8080/tcp
sudo firewall-cmd --reload
# Test from external machine
curl -v http://your-server-ip:8080/health
# Tests external connectivity
๐ Simple Commands Summary
Hereโs your Go deployment quick reference! ๐
Command | Purpose | Example |
---|---|---|
go build | Compile Go program | go build -o myapp main.go |
go run | Compile and run | go run main.go |
go get | Install packages | go get github.com/gin-gonic/gin |
go mod init | Initialize module | go mod init myproject |
go mod tidy | Clean dependencies | Removes unused dependencies |
systemctl start myapi | Start Go service | Starts systemd service |
journalctl -u myapi -f | View logs | Real-time log viewing |
go tool pprof | Profile application | Performance analysis |
๐ก Tips for Success
Follow these best practices for Go deployment success! ๐
๐ Build Optimization
- Use
-ldflags="-s -w"
to reduce binary size - Enable CGO_ENABLED=0 for fully static binaries
- Use build constraints for different environments
- Consider using UPX for further compression
โก Performance Tuning
- Set GOMAXPROCS appropriately for your server
- Use sync.Pool for object reuse
- Implement connection pooling for databases
- Monitor garbage collection with GODEBUG
๐ Security Best Practices
- Never run Go applications as root
- Use least privilege principles
- Validate all input rigorously
- Enable TLS for production APIs
๐ Monitoring and Observability
- Add structured logging with logrus or zap
- Implement health check endpoints
- Use pprof for performance profiling
- Set up metrics with Prometheus
๐ณ Container Best Practices
- Use multi-stage Docker builds
- Run as non-root user in containers
- Set resource limits appropriately
- Use distroless or alpine base images
๐ Deployment Strategies
- Implement graceful shutdown handling
- Use blue-green or rolling deployments
- Keep deployment artifacts versioned
- Automate with CI/CD pipelines
๐ What You Learned
Congratulations! Youโve mastered Go application deployment on AlmaLinux! ๐ Hereโs what you accomplished:
โ
Installed Go toolchain and set up development environment
โ
Built production-ready Go web applications with Gin framework
โ
Created systemd services for automatic startup and management
โ
Configured reverse proxy with Nginx for production serving
โ
Implemented Docker deployment with multi-stage builds
โ
Set up monitoring and profiling for performance optimization
โ
Secured applications with proper user permissions and practices
โ
Troubleshot common issues like a seasoned Go developer
๐ฏ Why This Matters
Go isnโt just a programming language - itโs the foundation of modern cloud-native infrastructure! ๐
From Docker to Kubernetes, from Terraform to countless microservices, Go powers the tools and systems that run todayโs internet. By mastering Go deployment on AlmaLinux, youโre positioning yourself at the center of this revolution.
Whether youโre building high-performance APIs, creating microservices, developing DevOps tools, or scaling systems to millions of users, Go gives you the speed and reliability that modern applications demand. The skills youโve learned today are incredibly valuable and will serve you throughout your career! ๐
Remember: Go was designed by Google to solve real-world scaling problems. Every Go application you deploy carries that DNA of performance, simplicity, and reliability. Keep building, keep optimizing, and enjoy the incredible developer experience that Go provides! โญ
Great job on completing this comprehensive Go deployment guide! Youโre now ready to deploy lightning-fast Go applications that scale to serve millions! ๐