Go is the backbone of modern cloud infrastructure. Over 75% of Cloud Native Computing Foundation projects are written in Go, including Kubernetes, Docker, and Prometheus. The language is built for high-performance, concurrent applications.
Deploying a Go application to production should be just as efficient as the language itself. With Out Plane, you can deploy your Go application in under a minute. This guide shows you exactly how.
What You'll Need
Before starting, make sure you have:
- Go 1.23+ installed on your machine
- A GitHub account
- A Go application in a GitHub repository
- A
go.modfile in your project root
Don't have Go installed? Download it from go.dev:
- Windows: Download the MSI installer and run it. Verify installation with
go versionin PowerShell. - macOS: Use
brew install goor download the PKG installer from go.dev - Linux: Run
wget https://go.dev/dl/go1.23.6.linux-amd64.tar.gz && sudo tar -C /usr/local -xzf go1.23.6.linux-amd64.tar.gz. Add/usr/local/go/binto your PATH.
Once Go is installed, create a project folder and initialize a module:
mkdir my-go-app && cd my-go-app
go mod init github.com/yourusername/my-go-appIf you don't have a Go application yet, you can use our example below.
Quick Start: Sample Go Application
Here's a minimal Go application you can use. Create these files in your repository:
Create main.go:
package main
import (
"context"
"encoding/json"
"log"
"net/http"
"os"
"os/signal"
"syscall"
"time"
)
type Response struct {
Message string `json:"message"`
Status string `json:"status"`
}
type HealthResponse struct {
Status string `json:"status"`
}
func homeHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
response := Response{
Message: "Hello from Go!",
Status: "running",
}
json.NewEncoder(w).Encode(response)
}
func healthHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
response := HealthResponse{
Status: "healthy",
}
json.NewEncoder(w).Encode(response)
}
func main() {
port := os.Getenv("PORT")
if port == "" {
port = "8080"
}
mux := http.NewServeMux()
mux.HandleFunc("/", homeHandler)
mux.HandleFunc("/health", healthHandler)
server := &http.Server{
Addr: ":" + port,
Handler: mux,
}
go func() {
log.Printf("Server starting on port %s", port)
if err := server.ListenAndServe(); err != http.ErrServerClosed {
log.Fatalf("Server failed to start: %v", err)
}
}()
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
log.Println("Server shutting down gracefully...")
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
if err := server.Shutdown(ctx); err != nil {
log.Fatalf("Server forced to shutdown: %v", err)
}
log.Println("Server stopped")
}Create go.mod:
module github.com/yourusername/my-go-app
go 1.23Test your application locally:
go run main.goVisit http://localhost:8080 in your browser. You should see the JSON response.
Push this code to a GitHub repository, and you're ready to deploy.
Optional: Add a Dockerfile
If you prefer full control over the build process, create a Dockerfile with multi-stage builds for smaller images:
# Build stage
FROM golang:1.23-alpine AS builder
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 .
# Runtime stage
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /app/main .
EXPOSE 8080
CMD ["./main"]This produces a production-ready image under 20MB. With a Dockerfile, select Dockerfile as your build method in the next step. Without one, select Buildpacks and Out Plane handles everything automatically.
Deploy in 3 Steps
Step 1: Connect Your Repository
- Go to console.outplane.com
- Sign in with your GitHub account
- Select your Go repository from the list
Out Plane automatically detects Go applications and configures the build process using Buildpacks.
Step 2: Configure Your Application
Configure the following settings in the create application form:
Build Method
Select how Out Plane should build your application:
- Buildpacks (Recommended): Automatically detects Go and compiles your application from
go.mod. No additional configuration needed. - Dockerfile: Use this if you have a custom
Dockerfilein your repository for full control over the build process.
For most Go applications, Buildpacks is the easiest option.
Basic Settings
- Port: Set to
8080 - Branch: Select
mainor your preferred branch - Region: Choose the region closest to your users (default: Nuremberg)
Environment Variables (Optional)
If your application uses environment-specific configuration, add them here. For our simple example, no environment variables are required.
For more complex applications, you might add variables like database URLs or API keys using the Add button or Raw Edit for bulk entry.
Step 3: Deploy
Click Deploy Application and watch the build process:
- Queued → Waiting for resources
- Building → Compiling your Go application
- Deploying → Starting your HTTP server
- Ready → Your app is live
Once the status shows Ready, your application is live. You can find your application URL at the top of the deployment page. Click the URL to open your Go app in a new tab. SSL is automatically configured.
Production Best Practices for Go Deployment
Use Go Modules Properly
Always commit your go.mod and go.sum files. These ensure reproducible builds across environments:
go mod tidy
git add go.mod go.sum
git commit -m "Update dependencies"The go.sum file locks dependency versions and prevents supply chain attacks.
Multi-Stage Docker Builds
If you use a custom Dockerfile, always use multi-stage builds. This separates the build environment from the runtime environment:
# Build with full Go toolchain
FROM golang:1.23-alpine AS builder
WORKDIR /app
COPY . .
RUN go build -o main .
# Run with minimal runtime
FROM alpine:latest
COPY --from=builder /app/main .
CMD ["./main"]This reduces your final image from 800MB to under 20MB. Smaller images deploy faster and use less bandwidth.
Graceful Shutdown Handling
Production Go applications must handle shutdown signals properly. This prevents data loss and incomplete requests:
package main
import (
"context"
"log"
"net/http"
"os"
"os/signal"
"syscall"
"time"
)
func main() {
server := &http.Server{
Addr: ":8080",
Handler: yourHandler,
}
go func() {
if err := server.ListenAndServe(); err != http.ErrServerClosed {
log.Fatalf("Server error: %v", err)
}
}()
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
log.Println("Shutting down gracefully...")
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
if err := server.Shutdown(ctx); err != nil {
log.Fatalf("Shutdown error: %v", err)
}
}This gives in-flight requests 30 seconds to complete before forcing shutdown.
Health Check Endpoints
Always expose a /health endpoint for load balancers and monitoring:
func healthHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(map[string]string{
"status": "healthy",
})
}Out Plane uses this endpoint to verify your application is ready to receive traffic.
Environment-Based Configuration
Read configuration from environment variables, not hardcoded values:
package main
import (
"os"
"strconv"
)
type Config struct {
Port string
DatabaseURL string
MaxConnections int
EnableDebugMode bool
}
func loadConfig() Config {
maxConns, _ := strconv.Atoi(getEnv("MAX_DB_CONNECTIONS", "10"))
debugMode, _ := strconv.ParseBool(getEnv("DEBUG_MODE", "false"))
return Config{
Port: getEnv("PORT", "8080"),
DatabaseURL: getEnv("DATABASE_URL", ""),
MaxConnections: maxConns,
EnableDebugMode: debugMode,
}
}
func getEnv(key, fallback string) string {
if value := os.Getenv(key); value != "" {
return value
}
return fallback
}This makes deployments across environments seamless without code changes.
Connecting a Database
Most Go applications need a database. Out Plane provides managed PostgreSQL:
- Go to Databases in the sidebar
- Click Create Database
- Select PostgreSQL version and region
- Copy the connection URL
Add the connection URL as an environment variable:
DATABASE_URL=postgres://user:password@host:5432/databaseThen use it in your Go application with database/sql and lib/pq:
package main
import (
"database/sql"
"log"
"os"
_ "github.com/lib/pq"
)
func initDatabase() *sql.DB {
databaseURL := os.Getenv("DATABASE_URL")
if databaseURL == "" {
log.Fatal("DATABASE_URL environment variable is not set")
}
db, err := sql.Open("postgres", databaseURL)
if err != nil {
log.Fatalf("Failed to connect to database: %v", err)
}
if err := db.Ping(); err != nil {
log.Fatalf("Failed to ping database: %v", err)
}
db.SetMaxOpenConns(25)
db.SetMaxIdleConns(5)
log.Println("Database connected successfully")
return db
}Add the PostgreSQL driver to your dependencies:
go get github.com/lib/pq
go mod tidyFor connection pooling and better performance, consider using pgx instead of lib/pq.
Custom Domain Setup
Replace the default .outplane.app URL with your own domain:
- Navigate to Domains
- Click Map Domain
- Enter your domain (e.g.,
api.yourdomain.com) - Add the DNS records shown to your domain registrar
SSL certificates are automatically provisioned once DNS propagates.
Monitoring Your Go Application
After deployment, monitor your Go application:
- Logs: View real-time application logs including HTTP request logs
- Metrics: Track CPU, memory, and network usage
- HTTP Logs: Analyze incoming requests, response times, and status codes
Access these from the sidebar in your application dashboard.
For advanced monitoring, Go's built-in profiling tools work seamlessly:
import _ "net/http/pprof"
func main() {
go func() {
log.Println(http.ListenAndServe("localhost:6060", nil))
}()
// Your application code
}This exposes profiling endpoints at /debug/pprof/ for CPU and memory analysis.
Troubleshooting
Application Won't Start
Check the port configuration. Make sure you set the port to 8080 in the application settings. Your Go code should read the PORT environment variable.
Module Errors During Build
Verify go.mod is in your repository root. Run go mod tidy locally to ensure all dependencies are tracked. Commit both go.mod and go.sum files.
Build Failures with Private Dependencies
Use a Dockerfile with build secrets. If your Go module imports private repositories, Buildpacks cannot authenticate. Use a custom Dockerfile with SSH keys or access tokens.
High Memory Usage
Check for goroutine leaks. Use runtime.NumGoroutine() to monitor goroutine count. Unbounded goroutines cause memory spikes. Always use contexts with timeouts for concurrent operations.
Next Steps
Your Go application is now deployed and running in production. Here's what to explore next:
- Scale your application: Adjust instance types and auto-scaling settings for higher traffic
- Set up CI/CD: Enable automatic deployments on every git push
- Add monitoring: Integrate with external monitoring tools via Out Plane's metrics export
- Deploy microservices: Use Out Plane to deploy multiple Go services with internal networking
For more deployment options and platform features, check out the Getting Started guide.
Summary
Deploying a Go application to Out Plane takes three steps:
- Connect your GitHub repository
- Configure port (8080) and environment variables
- Deploy and get your live URL
No server configuration, no manual SSL setup, no infrastructure management. Just push your code and go live.
Ready to deploy your Go application? Get started with Out Plane and receive $20 in free credit. Pay only for what you use with per-second billing.