Suga helps you migrate existing applications to a cloud-agnostic architecture. With Suga, you get local development with local emulation of cloud services, the ability to deploy anywhere - AWS or GCP from the same code, and auto-generated Terraform for production-ready infrastructure. This guide walks through migrating a Go web service as an example that can be easily adapted to other languages and services.
Initialize Suga Project
Navigate to your existing project directory and initialize Suga:
When prompted, provide:
Project name: my-app (or your preferred name)
Description: Brief description of your application
This creates a suga.yaml configuration file in your project root.
Open the Suga visual editor to design your infrastructure:
Build your infrastructure visually:
Drag an entrypoint onto the canvas and name it ingress
Add a service named app and connect the entrypoint to it
Add a bucket named files and connect the service to it
Configure bucket permissions to include read, write, and delete
Configure the service with a command to run locally (e.g., go run main.go or npm run dev)
This generates your suga.yaml configuration:
target : suga/aws@1
name : my-app
description : Your application description
services :
app :
container :
docker :
dockerfile : Dockerfile
context : .
dev :
script : go run main.go
buckets :
files :
access :
app :
- all
entrypoints :
ingress :
routes :
/ :
name : app
The visual editor automatically maintains valid YAML syntax and ensures proper resource relationships.
Add Container support
Create a Dockerfile for production deployment:
FROM golang:1.24-alpine as builder
WORKDIR /app
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o main main.go
FROM alpine:latest
RUN apk --no-cache add ca-certificates
COPY --from=builder /app/main .
ENTRYPOINT [ "/main" ]
If you already have a Dockerfile, ensure it’s referenced correctly in your suga.yaml configuration.
Generate SDK and Migrate code
Generate the Suga SDK for your project:
suga generate --go --go-out ./suga --go-package-name suga
Key migration steps:
Replace cloud SDK imports with import "example/suga"
Initialize Suga client with app, err := suga.NewClient()
Update API calls (e.g., S3 PutObject → app.Files.Write())
Simplify error handling (no need for AWS/GCP-specific error types)
Test each endpoint after changes
Now migrate your application code. Here’s a complete example showing the transformation:
Before: AWS SDK
Diff
After: Suga SDK
package main
import (
" context "
" fmt "
" log "
" net/http "
" os "
" strings "
" github.com/aws/aws-sdk-go-v2/aws "
" github.com/aws/aws-sdk-go-v2/config "
" github.com/aws/aws-sdk-go-v2/service/s3 "
)
func main () {
// Load AWS configuration
cfg , err := config . LoadDefaultConfig ( context . TODO ())
if err != nil {
log . Fatalf ( "Failed to load AWS config: %v " , err )
}
// Create S3 client
s3Client := s3 . NewFromConfig ( cfg )
bucketName := os . Getenv ( "S3_BUCKET_NAME" )
router := http . NewServeMux ()
router . HandleFunc ( "GET /hello/{name}" , func ( w http . ResponseWriter , r * http . Request ) {
name := r . PathValue ( "name" )
// AWS S3 PutObject
_ , err := s3Client . PutObject ( context . TODO (), & s3 . PutObjectInput {
Bucket : aws . String ( bucketName ),
Key : aws . String ( "example.txt" ),
Body : strings . NewReader ( "Hello, " + name + "!" ),
})
if err != nil {
log . Printf ( "Failed to upload to S3: %v " , err )
http . Error ( w , "Error uploading to S3" , http . StatusInternalServerError )
return
}
fmt . Fprintf ( w , "Hello, %s !" , name )
})
port := os . Getenv ( "PORT" )
if port == "" {
port = "8080"
}
log . Printf ( "Server starting on port %s \n " , port )
log . Fatal ( http . ListenAndServe ( ":" + port , router ))
}
package main
import (
+ "example/suga"
- "context"
"fmt"
"log"
"net/http"
"os"
- "strings"
- "github.com/aws/aws-sdk-go-v2/aws"
- "github.com/aws/aws-sdk-go-v2/config"
- "github.com/aws/aws-sdk-go-v2/service/s3"
)
func main() {
- // Load AWS configuration
- cfg, err := config.LoadDefaultConfig(context.TODO())
- if err != nil {
- log.Fatalf("Failed to load AWS config: %v", err)
- }
- // Create S3 client
- s3Client := s3.NewFromConfig(cfg)
- bucketName := os.Getenv("S3_BUCKET_NAME")
+ // Initialize Suga client
+ app, err := suga.NewClient()
+ if err != nil {
+ log.Fatalf("Failed to create suga client: %v", err)
+ }
router := http.NewServeMux()
router.HandleFunc("GET /hello/{name}", func(w http.ResponseWriter, r *http.Request) {
name := r.PathValue("name")
- // AWS S3 PutObject
- _, err := s3Client.PutObject(context.TODO(), &s3.PutObjectInput{
- Bucket: aws.String(bucketName),
- Key: aws.String("example.txt"),
- Body: strings.NewReader("Hello, " + name + "!"),
- })
- if err != nil {
- log.Printf("Failed to upload to S3: %v", err)
- http.Error(w, "Error uploading to S3", http.StatusInternalServerError)
- return
- }
+ // Simplified file write using Suga
+ if err := app.Files.Write("example.txt", []byte("Hello, "+name+"!")); err != nil {
+ log.Printf("Failed to write file: %v", err)
+ http.Error(w, "Error writing file", http.StatusInternalServerError)
+ return
+ }
fmt.Fprintf(w, "Hello, %s!", name)
})
port := os.Getenv("PORT")
if port == "" {
port = "8080"
}
log.Printf("Server starting on port %s\n", port)
log.Fatal(http.ListenAndServe(":"+port, router))
}
package main
import (
" example/suga "
" fmt "
" log "
" net/http "
" os "
)
func main () {
// Initialize Suga client
app , err := suga . NewClient ()
if err != nil {
log . Fatalf ( "Failed to create suga client: %v " , err )
}
router := http . NewServeMux ()
router . HandleFunc ( "GET /hello/{name}" , func ( w http . ResponseWriter , r * http . Request ) {
name := r . PathValue ( "name" )
// Simplified file write using Suga
if err := app . Files . Write ( "example.txt" , [] byte ( "Hello, " + name + "!" )); err != nil {
log . Printf ( "Failed to write file: %v " , err )
http . Error ( w , "Error writing file" , http . StatusInternalServerError )
return
}
fmt . Fprintf ( w , "Hello, %s !" , name )
})
port := os . Getenv ( "PORT" )
if port == "" {
port = "8080"
}
log . Printf ( "Server starting on port %s \n " , port )
log . Fatal ( http . ListenAndServe ( ":" + port , router ))
}
Remove all cloud provider SDK dependencies after migration to avoid conflicts and reduce bundle size.
Test Locally
Start the Suga development environment:
Test your migrated application:
curl http://localhost:3000/hello/world
Expected response:
Suga dev provides local emulation of cloud services. Files are stored locally during development and in real cloud storage when deployed.
Deploy to Cloud
Build your application and generate Terraform:
Select your cloud provider and region when prompted. You’ll see:
✓ Terraform generated successfully
output written to ./terraform/stacks/my-app
Next steps:
1. Run cd ./terraform/stacks/my-app to move to the stack directory
2. Initialize the stack terraform init -upgrade
3. Optionally, preview with terraform plan
4. Deploy with terraform apply
Configure cloud provider credentials:
gcloud auth application-default login
Deploy with Terraform:
cd terraform/stacks/my-app
Type yes when prompted. After deployment completes, your application endpoints will be displayed in the output.
Your application is now cloud-agnostic. Deploy to AWS or GCP without code
changes.
Next Steps
Add more resources like databases or queues to your suga.yaml
Configure environment-specific settings for staging and production
Explore Suga’s monitoring and observability features