{
"$type": "site.standard.document",
"bskyPostRef": {
"cid": "bafyreihjky46flqmxpmdml7gdpj4rrqxd4bbredzwqczpqzgtkyfo5r5si",
"uri": "at://did:plc:25rdn5elo5izoxrmtis34zuk/app.bsky.feed.post/3moysd5f2bf72"
},
"coverImage": {
"$type": "blob",
"ref": {
"$link": "bafkreihmydjlsxqb6vzgltnt6tmhcnfragoy62ash2pibglyvmv42s52ei"
},
"mimeType": "image/webp",
"size": 174732
},
"path": "/pavan_madduri/docker-init-ocir-oke-from-empty-folder-to-production-in-15-minutes-2o4e",
"publishedAt": "2026-06-24T01:36:06.000Z",
"site": "https://dev.to",
"tags": [
"docker",
"oci",
"devops",
"platformengineering",
"GitHub",
"LinkedIn",
"Website",
"Google Scholar",
"ResearchGate"
],
"textContent": "I timed myself. Starting from an empty directory with a Go application idea, how fast could I get to a running deployment on OKE? The answer was 14 minutes. `docker init` did more of the work than I expected.\n\n## What docker init Does\n\nIf you haven't used it, `docker init` is an interactive scaffolding tool built into Docker CLI. You run it in your project directory and it generates a Dockerfile, .dockerignore, and docker-compose.yml tuned for your language.\n\n\n\n mkdir oci-api && cd oci-api\n go mod init github.com/pmady/oci-api\n\n # Write a quick API\n cat > main.go << 'EOF'\n package main\n\n import (\n \"fmt\"\n \"log\"\n \"net/http\"\n \"os\"\n )\n\n func main() {\n http.HandleFunc(\"/\", func(w http.ResponseWriter, r *http.Request) {\n fmt.Fprintf(w, \"running on %s\", os.Getenv(\"OCI_REGION\"))\n })\n http.HandleFunc(\"/health\", func(w http.ResponseWriter, r *http.Request) {\n w.WriteHeader(200)\n })\n log.Fatal(http.ListenAndServe(\":8080\", nil))\n }\n EOF\n\n\nNow the magic part:\n\n\n\n $ docker init\n\n Welcome to the Docker Init CLI!\n\n ? What application platform does your project use? Go\n ? What version of Go do you want to use? 1.22\n ? What's the relative directory for your main package? .\n ? What port does your server listen on? 8080\n\n\nIt generates three files:\n\n**Dockerfile** — Multi-stage build, distroless base, non-root user. Actually good defaults. I've seen teams write worse Dockerfiles by hand.\n\n**compose.yaml** — Basic setup with port mapping and env vars.\n\n**.dockerignore** — Excludes .git, binaries, vendor directory. Reasonable.\n\nThe generated Dockerfile looked like this (slightly simplified):\n\n\n\n FROM golang:1.22 AS build\n WORKDIR /src\n COPY go.mod go.sum ./\n RUN go mod download\n COPY . .\n RUN CGO_ENABLED=0 go build -o /bin/server .\n\n FROM gcr.io/distroless/static-debian12:nonroot\n COPY --from=build /bin/server /bin/\n EXPOSE 8080\n USER nonroot:nonroot\n ENTRYPOINT [\"/bin/server\"]\n\n\nMulti-stage, static binary, distroless, non-root. I'd write almost the same thing myself. The only change I made was adding `-ldflags=\"-s -w\"` to strip debug symbols and shrink the binary.\n\n## Minute 0-5: Build and Test Locally\n\n\n docker compose up --build\n\n # In another terminal\n curl localhost:8080\n # running on\n\n curl localhost:8080/health\n # 200 OK\n\n\nWorks. Five minutes in and I have a containerized API running locally.\n\n## Minute 5-8: Push to OCIR\n\n\n # Login\n docker login iad.ocir.io -u '<tenancy-namespace>/pmady'\n\n # Tag\n docker tag oci-api-server:latest iad.ocir.io/<tenancy>/demos/oci-api:v1\n\n # Quick scan\n docker scout cves iad.ocir.io/<tenancy>/demos/oci-api:v1\n\n # Push\n docker push iad.ocir.io/<tenancy>/demos/oci-api:v1\n\n\nScout showed zero CVEs because distroless has almost nothing in it. Push took about 10 seconds because the image is 12MB.\n\n## Minute 8-14: Deploy to OKE\n\nI already had an OKE cluster running (if you don't, add 20 minutes for `oci ce cluster create`). The deployment manifest:\n\n\n\n # deploy.yaml\n apiVersion: apps/v1\n kind: Deployment\n metadata:\n name: oci-api\n spec:\n replicas: 2\n selector:\n matchLabels:\n app: oci-api\n template:\n metadata:\n labels:\n app: oci-api\n spec:\n containers:\n - name: api\n image: iad.ocir.io/<tenancy>/demos/oci-api:v1\n ports:\n - containerPort: 8080\n env:\n - name: OCI_REGION\n value: us-ashburn-1\n readinessProbe:\n httpGet:\n path: /health\n port: 8080\n periodSeconds: 5\n resources:\n requests:\n cpu: 100m\n memory: 64Mi\n limits:\n cpu: 500m\n memory: 128Mi\n imagePullSecrets:\n - name: ocir-secret\n ---\n apiVersion: v1\n kind: Service\n metadata:\n name: oci-api\n annotations:\n oci.oraclecloud.com/load-balancer-type: \"lb\"\n service.beta.kubernetes.io/oci-load-balancer-shape: \"flexible\"\n service.beta.kubernetes.io/oci-load-balancer-shape-flex-min: \"10\"\n service.beta.kubernetes.io/oci-load-balancer-shape-flex-max: \"100\"\n spec:\n type: LoadBalancer\n selector:\n app: oci-api\n ports:\n - port: 80\n targetPort: 8080\n\n\n\n kubectl apply -f deploy.yaml\n\n # Wait for LB IP\n kubectl get svc oci-api -w\n # NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S)\n # oci-api LoadBalancer 10.96.1.120 129.153.xx.xx 80:31234/TCP\n\n curl http://129.153.xx.xx/\n # running on us-ashburn-1\n\n\n14 minutes. Empty folder to public API on OKE.\n\n## What docker init Got Right\n\nI was skeptical about `docker init` being useful for anything beyond demos. But the generated Dockerfile was genuinely good:\n\n * **Multi-stage build** — keeps the final image small\n * **Distroless base** — minimal attack surface, near-zero CVEs\n * **Non-root user** — security best practice out of the box\n * **Separate dependency download** — `go mod download` before `COPY .` means dependencies are cached and rebuilds are fast\n\n\n\nThe only things I changed for OKE deployment were the `-ldflags` optimization and adding a health check endpoint (which `docker init` can't know about since it's application-specific).\n\n## What It Doesn't Do\n\n`docker init` handles the Docker side. It doesn't generate:\n\n * Kubernetes manifests\n * CI/CD pipeline config\n * OCIR login/push scripts\n * Terraform for infrastructure\n\n\n\nThat's fair. It's a Docker tool, not a platform tool. But the Dockerfile it generates is solid enough that I don't need to edit it for most Go and Python projects.\n\n## Languages I've Tested\n\nLanguage | Quality of Generated Dockerfile | Notes\n---|---|---\nGo | Excellent | Multi-stage, static binary, distroless\nPython | Good | Uses slim base, proper requirements.txt handling\nNode.js | Good | Multi-stage, npm ci for production\nRust | Excellent | cargo-chef for caching, musl for static binary\nJava | Decent | Uses Eclipse Temurin, could use jlink for smaller images\n\nGo and Rust output is good enough to use as-is. Python and Node need minor tweaks depending on your framework. Java needs the most work.\n\n## My Workflow Now\n\nFor quick services and prototypes, this is my default:\n\n\n\n mkdir project && cd project\n # write code\n docker init\n # tweak Dockerfile if needed\n docker compose up --build # test locally\n docker push ... # push to OCIR\n kubectl apply -f deploy.yaml # deploy to OKE\n\n\nThe gap between \"it works on my laptop\" and \"it's running on OKE\" is smaller than it's ever been.\n\n_Pavan Madduri — Oracle ACE Associate, CNCF Golden Kubestronaut. GitHub | LinkedIn | Website | Google Scholar | ResearchGate_",
"title": "docker init OCIR OKE: From Empty Folder to Production in 15 Minutes"
}