Deploy a Backend API
This guide walks through deploying a Go HTTP API to Satusky. By the end you’ll have a running service with HTTPS, environment variables, and a custom domain. The same pattern applies to any backend language — Node.js, Python, Rust — as long as it listens on a TCP port.
Prerequisites
Section titled “Prerequisites”1ctlinstalled — see Installation- Authenticated with a valid API token — see Authentication
- A project directory with your application code
Step 1: Write a Dockerfile
Section titled “Step 1: Write a Dockerfile”Satusky builds your image in the cloud using Kaniko — you do not need Docker installed locally. You just need a Dockerfile in your project root.
Here’s a production-ready multi-stage Dockerfile for a Go service:
FROM golang:1.22-alpine AS builderWORKDIR /appCOPY go.mod go.sum ./RUN go mod downloadCOPY . .RUN go build -o server .
FROM alpine:3.19WORKDIR /appCOPY --from=builder /app/server .EXPOSE 8080CMD ["./server"]The multi-stage build keeps the final image small. The builder stage compiles the binary; the second stage ships only the binary on a minimal Alpine base.
Step 2: Initialize the project
Section titled “Step 2: Initialize the project”Run 1ctl init from the project root. The CLI auto-detects the app name from the directory and writes satusky.toml:
1ctl initExpected output:
✅ Created satusky.toml💡 Edit satusky.toml, then run: 1ctl deployThe generated satusky.toml:
[app] name = "my-api" port = 8080Edit this file to set your desired resource limits and any other fields before deploying. The defaults are a solid starting point for most APIs. You can always bump these with deploy flags or by editing the file:
[app] name = "my-api" port = 8080 dockerfile = "Dockerfile" cpu = "0.5" memory = "256Mi"Step 3: Deploy
Section titled “Step 3: Deploy”1ctl deploy1ctl deploy packages your project source, uploads it to Satusky’s build service, runs Kaniko to build and push the image, then creates a Kubernetes Deployment, Service, and public route in your namespace.
Expected output:
💡 Packaging build context...💡 Submitting build to cloud...💡 Build ID: bld_07x2p9k1n4Step 1/5: Building image (cloud) my-api ✓Step 2/5: Creating/updating deployment my-api ✓Step 3/5: Configuring services my-api ✓Step 4/5: Setting up environment and storage my-api ✓Step 5/5: Configuring public routing and dependencies my-api ✓💡 Generated new domain: happyotter-x3k9m2.satusky.com✅ 🚀 Deployment for my-api is successful!💡 App URL: https://happyotter-x3k9m2.satusky.comDeployment ID: 7f1fab9e-5f87-4612-b306-3da846b95d18The domain is a randomly generated subdomain — not derived from your app name. Treat the printed URL as usable only after DNS and routing are ready. 1ctl deploy --wait confirms pod health; public URL readiness is a separate check. Verify with:
curl -fsS https://happyotter-x3k9m2.satusky.com/healthStep 4: Check deployment status
Section titled “Step 4: Check deployment status”1ctl deploy getExpected output:
Deployment Details──────────────────Deployment ID: 7f1fab9e-5f87-4612-b306-3da846b95d18Status: completedURL: https://happyotter-x3k9m2.satusky.comType: productionVersion: latestPort: 8080CPU Request: 0.5Memory Request: 256MiMemory Limit: 256MiCreated: just nowLast Updated: just nowThe build status cycles through pending → building → deploying → completed. If it sticks at building for more than a few minutes, check logs to see if there’s a compile error.
For a quick K8s live status check:
1ctl deploy statusStatus: RunningMessage: Deployment is running normallyProgress: 100%Step 5: View logs
Section titled “Step 5: View logs”1ctl logsExpected output:
Pod Logs────────[2026-04-27 10:00:05] [my-api-7c9d84-fkp3j] server listening on :8080[2026-04-27 10:00:12] [my-api-7c9d84-fkp3j] GET /health 200 88µs[2026-04-27 10:00:31] [my-api-7c9d84-fkp3j] GET /users 200 4.2ms---Showing last 100 linesFor a live stream (equivalent to tail -f):
1ctl logs streamPress Ctrl+C to stop the stream. Pass --tail 50 to limit the snapshot to 50 lines.
Step 6: Add environment variables
Section titled “Step 6: Add environment variables”Non-sensitive config like DATABASE_HOST, LOG_LEVEL, or feature flags:
1ctl env create \ --env LOG_LEVEL=info \ --env APP_ENV=productionExpected output:
✅ Environment my-api created successfullyFor sensitive values — passwords, API keys, tokens — use secrets instead:
1ctl secret create \ --kv DATABASE_PASSWORD=s3cr3t \ --kv JWT_SECRET=abc123xyzExpected output:
✅ Secret my-api created successfullySecrets are encrypted at rest and never appear in 1ctl output after creation.
After setting env vars or secrets, trigger a restart to pick them up:
1ctl deploy restartExpected output:
💡 Initiating rolling restart for deployment <id>...✅ Rolling restart initiated. Pods are being replaced one by one.💡 Use '1ctl deploy status --deployment-id <id>' to monitor progress.Step 7: Add a custom domain
Section titled “Step 7: Add a custom domain”Point your API at a real domain instead of the default *.satusky.com subdomain:
1ctl deploy --domain api.myapp.comSatusky updates the public route to send api.myapp.com to your deployment. cert-manager provisions a Let’s Encrypt certificate automatically. See the Custom Domains guide for DNS setup details.
Verify the certificate is valid:
curl -I https://api.myapp.comHTTP/2 200server: nginx...What just happened
Section titled “What just happened”- Cloud build —
1ctl deployuploaded your source to Satusky’s build service. Kaniko built the Docker image inside the cluster with no Docker daemon on your machine. - Image push — The built image landed in Satusky’s private registry at
registry.satusky.com. - Kubernetes deployment — Satusky created a Deployment, Service, and public route in your namespace. Resource limits came from
satusky.toml. - Automatic HTTPS — cert-manager provisioned a subdomain and TLS certificate. HTTPS is on by default.
Next steps
Section titled “Next steps”- Environment Configuration — manage staging vs production config
- Autoscaling — add HPA to handle traffic spikes
- CI/CD Integration — automate deploys from GitHub Actions
- Custom Domains — full DNS setup walkthrough