Skip to content

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.

  • 1ctl installed — see Installation
  • Authenticated with a valid API token — see Authentication
  • A project directory with your application code

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 builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN go build -o server .
FROM alpine:3.19
WORKDIR /app
COPY --from=builder /app/server .
EXPOSE 8080
CMD ["./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.

Run 1ctl init from the project root. The CLI auto-detects the app name from the directory and writes satusky.toml:

Terminal window
1ctl init

Expected output:

✅ Created satusky.toml
💡 Edit satusky.toml, then run: 1ctl deploy

The generated satusky.toml:

[app]
name = "my-api"
port = 8080

Edit 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"
Terminal window
1ctl deploy

1ctl 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_07x2p9k1n4
Step 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.com
Deployment ID: 7f1fab9e-5f87-4612-b306-3da846b95d18

The 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:

Terminal window
curl -fsS https://happyotter-x3k9m2.satusky.com/health
Terminal window
1ctl deploy get

Expected output:

Deployment Details
──────────────────
Deployment ID: 7f1fab9e-5f87-4612-b306-3da846b95d18
Status: completed
URL: https://happyotter-x3k9m2.satusky.com
Type: production
Version: latest
Port: 8080
CPU Request: 0.5
Memory Request: 256Mi
Memory Limit: 256Mi
Created: just now
Last Updated: just now

The build status cycles through pendingbuildingdeployingcompleted. 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:

Terminal window
1ctl deploy status
Status: Running
Message: Deployment is running normally
Progress: 100%
Terminal window
1ctl logs

Expected 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 lines

For a live stream (equivalent to tail -f):

Terminal window
1ctl logs stream

Press Ctrl+C to stop the stream. Pass --tail 50 to limit the snapshot to 50 lines.

Non-sensitive config like DATABASE_HOST, LOG_LEVEL, or feature flags:

Terminal window
1ctl env create \
--env DATABASE_URL=postgres://user:[email protected]:5432/mydb \
--env LOG_LEVEL=info \
--env APP_ENV=production

Expected output:

✅ Environment my-api created successfully

For sensitive values — passwords, API keys, tokens — use secrets instead:

Terminal window
1ctl secret create \
--kv DATABASE_PASSWORD=s3cr3t \
--kv JWT_SECRET=abc123xyz

Expected output:

✅ Secret my-api created successfully

Secrets 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:

Terminal window
1ctl deploy restart

Expected 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.

Point your API at a real domain instead of the default *.satusky.com subdomain:

Terminal window
1ctl deploy --domain api.myapp.com

Satusky 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:

Terminal window
curl -I https://api.myapp.com
HTTP/2 200
server: nginx
...
  1. Cloud build1ctl deploy uploaded your source to Satusky’s build service. Kaniko built the Docker image inside the cluster with no Docker daemon on your machine.
  2. Image push — The built image landed in Satusky’s private registry at registry.satusky.com.
  3. Kubernetes deployment — Satusky created a Deployment, Service, and public route in your namespace. Resource limits came from satusky.toml.
  4. Automatic HTTPS — cert-manager provisioned a subdomain and TLS certificate. HTTPS is on by default.