Skip to content

Build Context & .dockerignore

When you run 1ctl deploy without --image, the CLI packages your project directory and sends it to Satusky’s cloud build service, which builds your container image using Kaniko. No local Docker installation is required.

Understanding what gets packaged — and how to control it — directly affects deploy speed and build cache efficiency.


1ctl deploy
├─ 1. Package project directory → build.tar.gz (respects .dockerignore)
├─ 2. Upload → POST /v1/cli/builds
├─ 3. Kaniko job runs your Dockerfile in-cluster
├─ 4. Build logs stream to your terminal in real time
├─ 5. Finished image pushed to registry.satusky.com/{namespace}/{app}:{tag}
└─ 6. Image reference passed to deployment upsert

Every step runs in the cloud. The only local operations are packaging the context and streaming the log output.


Without a .dockerignore, the CLI includes everything in the current directory, recursively. Common directories that inflate context size unnecessarily:

Directory / patternTypical sizeProblem
node_modules/50 MB – 1 GBInstalled by npm install in Dockerfile; uploading is redundant
.git/VariableNot useful inside the image
venv/ / .venv/20 MB – 500 MBPython virtualenv; same problem as node_modules
data/VariableTraining data, fixtures — usually not needed at build time
*.ipynbVariableJupyter notebooks; rarely needed in the production image
model/1 GB+Local model weights — should be downloaded at build time

Create a .dockerignore file in your project root. The syntax is identical to .gitignore. Files and directories matching any pattern are excluded from the build context before upload — they are never sent to the build service.

.dockerignore patterns are evaluated against paths relative to the project root. A leading / anchors to the root; without it, the pattern matches anywhere in the tree.

node_modules
.git
.gitignore
*.md
.env
.env.*
npm-debug.log
dist
coverage
.nyc_output
.DS_Store
__pycache__
*.pyc
*.pyo
*.pyd
.git
.gitignore
.env
venv/
.venv/
*.egg-info
dist/
build/
.pytest_cache
.mypy_cache
.DS_Store
__pycache__
*.pyc
data/
notebooks/
*.ipynb
model/
checkpoints/
.git
.env
.DS_Store

For ML projects, exclude model/ and data/ from the build context entirely. Download weights at build time instead (see below).


Instead of COPY-ing large files (which requires them in the build context), download them during the RUN step in your Dockerfile. Docker layer caching means the download only runs again when the RUN instruction or its dependencies change.

FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Download model weights at build time — not from the local filesystem
RUN python -c "from transformers import AutoModelForCausalLM; \
AutoModelForCausalLM.from_pretrained('my-org/my-model')"
COPY . .
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8080"]

The advantages:

  • Build context stays small (fast uploads on every deploy).
  • The weights layer is cached after the first build — subsequent deploys only rebuild layers that changed.
  • The final image still contains the weights; there is no runtime download penalty.

If you already have an image built and pushed — by your own CI pipeline or a previous 1ctl deploy — skip the cloud build entirely:

Terminal window
1ctl deploy --image registry.satusky.com/org-acme-prod/my-api:abc1234

With --image, steps 1–5 of the build pipeline are skipped. The CLI calls the deployment upsert directly with the provided image reference. This is the fastest possible deploy path.

The --image flag accepts any image reference accessible from the cluster’s network, including images in Satusky’s private registry, Docker Hub public images, and images in other registries that don’t require authentication. For private external registries, contact support.


Dockerfile best practices for fast rebuilds

Section titled “Dockerfile best practices for fast rebuilds”

Layer ordering determines what gets cached. Docker (and Kaniko) cache each layer independently. A change to a layer invalidates all layers below it.

Recommended ordering:

# 1. Base image — changes rarely
FROM node:22-alpine
WORKDIR /app
# 2. Dependency manifest — changes less often than source
COPY package.json package-lock.json ./
# 3. Install dependencies — heavy, but cached until manifest changes
RUN npm ci --production
# 4. Source code — changes most often, so it goes last
COPY . .
# 5. Build step (if needed)
RUN npm run build
CMD ["node", "dist/server.js"]

If you COPY . . before npm ci, any source change busts the install cache and triggers a full reinstall on every deploy.

Multi-stage builds reduce final image size by separating the build environment from the runtime environment:

# Build stage
FROM node:22-alpine AS builder
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build
# Runtime stage — only what the app needs to run
FROM node:22-alpine AS runtime
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
CMD ["node", "dist/server.js"]

The runtime image excludes dev dependencies, build tools, and intermediate artifacts.


Before deploying, you can estimate the build context size by running:

Terminal window
tar -czf - --exclude-from=.dockerignore . | wc -c

This pipes a dry-run tarball through wc to show the byte count. The actual upload uses the same exclusion logic as your .dockerignore.

As a rough target: build contexts under 10 MB upload in under a second on a typical connection. Contexts over 100 MB noticeably slow down every deploy.