migetpacks builds your application in four phases. Each phase is handled by a dedicated script inside the builder container.

Phase 1: Language Detection

The bin/detect script scans your source directory for language-specific marker files. It checks each language in priority order and returns the first match.
File PresentDetected Language
Dockerfiledockerfile
compose.yaml / docker-compose.ymlcompose
Gemfileruby
package.jsonnodejs
deno.json / deno.jsoncdeno
bun.lockb / bunfig.tomlbun
requirements.txt / pyproject.toml / setup.pypython
go.modgo
Cargo.tomlrust
pom.xml / build.gradlejava
build.gradle.ktskotlin
build.sbtscala
project.cljclojure
*.csproj / *.sln / *.slnxdotnet
mix.exselixir
composer.jsonphp
If a Dockerfile is present, migetpacks uses it directly instead of generating one. Set LANGUAGE explicitly to override this behavior.

Phase 2: Version Detection

The bin/detect-version script reads language-specific version files and normalizes them for Docker image tags.
LanguageVersion Sources (priority order)
Node.js.node-version.nvmrcpackage.json engines.node
Ruby.ruby-versionGemfile ruby declaration
Python.python-versionruntime.txtuv.lockpyproject.toml
Go.go-versiongo.mod go directive
Rustrust-toolchainrust-toolchain.toml channel
Java.java-versionsystem.propertiespom.xml
.NETglobal.json*.csproj TargetFramework
Elixir.elixir-version.tool-versions (asdf)
Deno.deno-version.dvmrcdeno.json version
Bun.bun-versionbunfig.toml version
PHP.php-versioncomposer.json require.php

Phase 3: Dockerfile Generation

The bin/build script generates an optimized multi-stage Dockerfile using the detected language and version. The generated Dockerfile follows best practices for layer caching.

Example: Node.js Application

For a Node.js app with version 20 detected from .nvmrc, migetpacks generates:
# Stage 1: Builder
FROM node:20 AS builder
WORKDIR /build

# Dependencies first (cached when lockfile unchanged)
COPY package.json package-lock.json ./
RUN npm ci

# Source code (changes frequently)
COPY . .
RUN npm run build && npm prune --production

# Cleanup dev/test files
RUN rm -rf .git .github test tests spec __tests__ coverage .nyc_output .cache

# Stage 2: Runtime (minimal image)
FROM node:20-slim
RUN groupadd -g 1000 miget && useradd -u 1000 -g miget -m miget
WORKDIR /app

COPY --from=builder --chown=miget:miget /build .

USER miget
EXPOSE 5000
CMD ["node", "server.js"]

Layer Caching Strategy

The key optimization is separating dependency installation from source code copying:
Step 1: COPY lockfiles     → Changes rarely     → CACHED
Step 2: RUN install deps   → Depends on Step 1  → CACHED (if lockfiles unchanged)
Step 3: COPY source code   → Changes frequently → Rebuilt
Step 4: RUN build + prune  → Depends on Step 3  → Rebuilt
This means that when only your source code changes (the common case), dependency installation is skipped entirely.

Custom Environment Variables

Any environment variable not in the known builder vars list is automatically injected into the generated Dockerfile:
# Pass build-time env vars
docker run --rm \
  -v ./app:/workspace/source \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -e OUTPUT_IMAGE=my-app:local \
  -e NODE_OPTIONS="--max-old-space-size=4096" \
  -e NEXT_PUBLIC_API_URL="https://api.example.com" \
  miget/migetpacks:latest
These become ENV statements in the generated Dockerfile, available at both build time and runtime.

Phase 4: Build and Push

The final phase uses Docker-in-Docker with BuildKit to build and push the image.
1

Start Docker daemon

A Docker-in-Docker daemon starts inside the builder container with BuildKit enabled.
2

Configure registry access

Registry credentials and mirrors are configured in the daemon. If REGISTRY_MIRROR is set, it is added as a pull-through cache.
3

Build with BuildKit

The generated Dockerfile is built with docker buildx build. Cache is read from and written to CACHE_IMAGE if configured.
4

Push or load

If OUTPUT_IMAGE contains a registry prefix, the image is pushed. Otherwise, it is loaded into the local Docker daemon.
5

Write results

If RESULT_FILE is set, build metadata (status, ports, processes, timing) is written as JSON for downstream CI/CD steps.

Build Cache Options

docker run --rm \
  -v ./app:/workspace/source \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -e OUTPUT_IMAGE=registry.io/my-app:latest \
  -e CACHE_IMAGE=registry.io/my-app:cache \
  miget/migetpacks:latest

Runtime Container

The built container follows these conventions:
PropertyValue
Usermiget (uid 1000)
Working directory/app
Default port5000
Process managerNone (single process)
Init systemNone (PID 1 is your app)
When USE_DHI=true, the runtime container is distroless (no shell, no package manager) and runs as the nonroot user instead.

Next Steps