Go API Server + Container Registry

    In this tutorial, we will create a Go API server and deploy it to a Pilvio VM as a Docker container. Container Registry is currently under development at Pilvio -- for now, we will use Docker Hub or a locally built image.

    Coming soon: Pilvio Container Registry will allow you to host container images directly within the Pilvio infrastructure in the future. For now, we use Docker Hub or build the image directly on the server.

    What We're Building

    • Go REST API with the chi router
    • Application packaged with Docker
    • Deployment to a Pilvio VM using cloud-init

    Prerequisites

    • Pilvio account and API token (see overview)
    • Go 1.22+ installed locally
    • Docker installed locally (optional)

    Step 1: Creating the Go API Application

    On your local machine:

    mkdir pilvio-go-api && cd pilvio-go-api
    go mod init pilvio-go-api
    go get github.com/go-chi/chi/v5
    go get github.com/go-chi/cors
    

    Create the file main.go:

    package main
    
    import (
    	"encoding/json"
    	"fmt"
    	"log"
    	"net/http"
    	"os"
    	"time"
    
    	"github.com/go-chi/chi/v5"
    	"github.com/go-chi/chi/v5/middleware"
    	"github.com/go-chi/cors"
    )
    
    func main() {
    	port := os.Getenv("PORT")
    	if port == "" {
    		port = "8080"
    	}
    
    	r := chi.NewRouter()
    
    	// Vahevara
    	r.Use(middleware.Logger)
    	r.Use(middleware.Recoverer)
    	r.Use(middleware.Timeout(30 * time.Second))
    	r.Use(cors.Handler(cors.Options{
    		AllowedOrigins: []string{"*"},
    		AllowedMethods: []string{"GET", "POST", "PUT", "DELETE"},
    		AllowedHeaders: []string{"Content-Type", "X-API-Key"},
    	}))
    
    	// Avalikud endpointid
    	r.Get("/health", func(w http.ResponseWriter, r *http.Request) {
    		json.NewEncoder(w).Encode(map[string]string{
    			"status":    "ok",
    			"timestamp": time.Now().Format(time.RFC3339),
    		})
    	})
    
    	// Kaitstud endpointid
    	r.Group(func(r chi.Router) {
    		r.Use(apiKeyAuth)
    
    		r.Get("/api/v1/items", listItems)
    		r.Post("/api/v1/items", createItem)
    	})
    
    	log.Printf("Go API server käivitatud pordil %s", port)
    	log.Fatal(http.ListenAndServe(fmt.Sprintf(":%s", port), r))
    }
    
    // Lihtne API võtme kontroll
    func apiKeyAuth(next http.Handler) http.Handler {
    	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    		key := r.Header.Get("X-API-Key")
    		expected := os.Getenv("APP_API_KEY")
    		if key == "" || key != expected {
    			http.Error(w, `{"error":"Autentimine ebaõnnestus"}`, http.StatusUnauthorized)
    			return
    		}
    		next.ServeHTTP(w, r)
    	})
    }
    
    // Näiteks mälusisesed andmed (tootmises kasuta andmebaasi)
    var items []map[string]string
    
    func listItems(w http.ResponseWriter, r *http.Request) {
    	w.Header().Set("Content-Type", "application/json")
    	json.NewEncoder(w).Encode(map[string]interface{}{"items": items})
    }
    
    func createItem(w http.ResponseWriter, r *http.Request) {
    	var item map[string]string
    	if err := json.NewDecoder(r.Body).Decode(&item); err != nil {
    		http.Error(w, `{"error":"Vigane JSON"}`, http.StatusBadRequest)
    		return
    	}
    	items = append(items, item)
    	w.WriteHeader(http.StatusCreated)
    	json.NewEncoder(w).Encode(item)
    }
    

    Step 2: Packaging with Docker

    Create the file Dockerfile:

    FROM golang:1.22-alpine AS builder
    WORKDIR /app
    COPY go.mod go.sum ./
    RUN go mod download
    COPY . .
    RUN CGO_ENABLED=0 GOOS=linux go build -o server .
    
    FROM alpine:3.19
    RUN apk --no-cache add ca-certificates
    WORKDIR /app
    COPY --from=builder /app/server .
    EXPOSE 8080
    CMD ["./server"]
    

    Build and test locally:

    docker build -t pilvio-go-api .
    docker run -p 8080:8080 -e APP_API_KEY=test123 pilvio-go-api
    

    Step 3: Deploying to a Pilvio VM

    Option A: Using Cloud-init (Automatic)

    Create a VM with a cloud-init script that installs Docker and starts the application:

    curl "https://api.pilvio.com/v1/user-resource/vm" \
      -H "apikey: SINU_PILVIO_TOKEN" \
      -X POST \
      -d "name=go-api-server" \
      -d "os_name=ubuntu" \
      -d "os_version=24.04" \
      -d "vcpu=2" \
      -d "ram=2048" \
      -d "disks=20" \
      -d "username=deploy" \
      -d "password=TurvalineParool123!" \
      -d 'cloud_init={
        "runcmd": [
          "curl -fsSL https://get.docker.com | sh",
          "usermod -aG docker deploy"
        ]
      }'
    

    After the VM is created and Docker is installed (wait approximately 2 minutes):

    ssh deploy@SINU_FLOATING_IP
    
    # Kopeeri projekti failid serverisse (lokaalarvutist)
    # scp -r ./* deploy@SINU_FLOATING_IP:~/go-api/
    
    # Ehita image otse serveris
    cd ~/go-api
    docker build -t pilvio-go-api .
    docker run -d \
      --name go-api \
      --restart unless-stopped \
      -p 8080:8080 \
      -e APP_API_KEY=sinu-tugev-api-vooti \
      pilvio-go-api
    

    Option B: Via Docker Hub

    If you are using Docker Hub (or another registry):

    # Lokaalselt
    docker tag pilvio-go-api sinu-kasutaja/pilvio-go-api:latest
    docker push sinu-kasutaja/pilvio-go-api:latest
    
    # Serveris
    docker pull sinu-kasutaja/pilvio-go-api:latest
    docker run -d \
      --name go-api \
      --restart unless-stopped \
      -p 8080:8080 \
      -e APP_API_KEY=sinu-tugev-api-vooti \
      sinu-kasutaja/pilvio-go-api:latest
    

    Step 4: Docker Compose for Production

    Create the file docker-compose.yml:

    services:
      api:
        build: .
        restart: unless-stopped
        ports:
          - "127.0.0.1:8080:8080"
        environment:
          - PORT=8080
          - APP_API_KEY=${APP_API_KEY}
        healthcheck:
          test: ["CMD", "wget", "-q", "--spider", "http://localhost:8080/health"]
          interval: 30s
          timeout: 5s
          retries: 3
    
      nginx:
        image: nginx:alpine
        restart: unless-stopped
        ports:
          - "80:80"
          - "443:443"
        volumes:
          - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
          - certbot-data:/etc/letsencrypt:ro
        depends_on:
          - api
    
    volumes:
      certbot-data:
    
    docker compose up -d
    

    Testing

    # Health check
    curl http://SINU_FLOATING_IP:8080/health
    
    # Elemendi lisamine
    curl -X POST http://SINU_FLOATING_IP:8080/api/v1/items \
      -H "X-API-Key: sinu-tugev-api-vooti" \
      -H "Content-Type: application/json" \
      -d '{"name": "Test", "description": "Pilvio Go API näide"}'
    
    # Elementide loetelu
    curl -H "X-API-Key: sinu-tugev-api-vooti" http://SINU_FLOATING_IP:8080/api/v1/items
    

    Container Registry (coming soon): When Pilvio Container Registry becomes available, you will be able to host your images directly within the Pilvio infrastructure without external registries. This reduces latency and keeps your data in Estonia.

    Next steps: Add a PostgreSQL database to replace in-memory data, or use StorageVault for file storage.