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.