Docker hosting in India: deploy any Dockerfile
Commit a Dockerfile at the root of your repository and we build the image from it and run it as your app. Go, Rust, Java, Ruby, Deno or a language we have no buildpack for: if it fits in one container, it gets HTTPS, custom domains, environment variables, logs and a managed PostgreSQL database.
From ₹100/month + 18% GST, PostgreSQL included, billed in INR.
Deploy Docker in 5 steps
Commit a Dockerfile and connect the repository
Place the Dockerfile in the repository root, not in a subfolder, and add a .dockerignore that leaves out .git, local dependencies and build output. Create an app, connect GitHub and pick the branch, or upload the same folder as a tar.gz with a deploy token.
The Dockerfile builder is selected
A Dockerfile at the root means the app is built from your image recipe instead of a buildpack, even if the repository also has a package.json or requirements.txt. Multi-stage builds work, and only the final stage ends up running.
Listen on port 5000
Every app is routed to port 5000 inside its container, whatever EXPOSE says. Make the server bind to 0.0.0.0 on the port in PORT with 5000 as the fallback, so the same image also runs on your laptop.
Pass configuration and DATABASE_URL at runtime
Keep secrets out of the Dockerfile. Set them as environment variables in the dashboard and read them when the process starts; when you attach PostgreSQL 16, its connection string reaches the container the same way, as DATABASE_URL.
Connect your domain, no certificates in the image
Add your domain, publish the TXT record to verify it, then point an A record or CNAME at the app. HTTPS is terminated in front of your container, so the image serves plain HTTP on port 5000 and never needs a certificate or port 443.
A minimal Docker app that deploys
FROM golang:1.23-alpine AS build
WORKDIR /src
COPY go.mod ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o /out/server .
FROM alpine:3.20
COPY --from=build /out/server /usr/local/bin/server
ENV PORT=5000
EXPOSE 5000
CMD ["server"]package main
import (
"fmt"
"net/http"
"os"
)
func main() {
port := os.Getenv("PORT")
if port == "" {
port = "5000"
}
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "ok")
})
http.ListenAndServe("0.0.0.0:"+port, nil)
}module example.com/server
go 1.23Docker gotchas to know before you push
No docker-compose: one image per app
A docker-compose.yml in the repository is ignored. Run the database as the managed PostgreSQL attached to the app instead of a postgres service, and run a second service as a second app on your plan.
EXPOSE does not choose the port
The router always sends traffic to port 5000. A container listening on 80 or 8080 builds, starts and reports running, then answers every request with a 502.
Persistent files go in /app/data
Each app has a volume mounted at /app/data, whatever your WORKDIR is; everything else in the container filesystem is reset on each deploy. The volume is owned by uid 32767, so an image that switches to a non-root USER must run as that uid to write there.
Order layers for fast rebuilds
Copy the dependency manifest and install dependencies before COPY . . so an ordinary code change reuses the cached dependency layer. Pin base image tags, such as alpine:3.20, rather than latest, so a rebuild does not change your runtime by surprise.