--- url: https://migetpacks.com/configuration/app-json --- --- title: "app.json" description: "Application manifest support" --- migetpacks supports `app.json` as an application manifest file. This file defines environment variables, process formation, deployment scripts, and buildpack configuration for your application. ## Supported Fields | Field | Description | |-------|-------------| | `env` | Environment variables for build and runtime | | `formation` | Process configuration with replicas and sizing | | `scripts` | Deployment lifecycle scripts | | `buildpacks` | Buildpack (language) ordering | | `addons` | Service dependencies (databases, caches, etc.) | ## Environment Variables The `env` field defines environment variables that are exported during the build and included in the [result JSON](/reference/result-json). Each variable can specify a fixed value, a generator, or be marked as required. ```json { "env": { "RAILS_ENV": { "value": "production" }, "SECRET_KEY_BASE": { "generator": "secret" }, "DATABASE_URL": { "required": true }, "OPTIONAL_VAR": { "description": "An optional config variable" } } } ``` ### Variable Properties | Property | Description | |----------|-------------| | `value` | A fixed value for the variable. Exported during build and included in result JSON. | | `generator` | Set to `"secret"` to auto-generate a random secret value. The generated value appears in the result JSON. | | `required` | Set to `true` to indicate this variable must be provided at deploy time. Appears in result JSON with a `null` value. | | `description` | Human-readable description (informational only, not used during build). | ## Formation The `formation` field defines process types with their replica count and compute size. This information is passed through to the result JSON for use by deployment orchestrators. ```json { "formation": { "web": { "quantity": 2, "size": "standard-1x" }, "worker": { "quantity": 1, "size": "standard-2x" }, "clock": { "quantity": 1 } } } ``` ### Formation Properties | Property | Description | |----------|-------------| | `quantity` | Number of replicas (instances) for this process type | | `size` | Compute size identifier (e.g., `standard-1x`, `performance-m`) | ## Scripts The `scripts` field defines lifecycle hooks that run at specific points during deployment. These are included in the result JSON for the deployment system to execute. ```json { "scripts": { "postdeploy": "bundle exec rails db:migrate", "pr-predestroy": "bundle exec rails db:drop" } } ``` ### Supported Scripts | Script | When It Runs | |--------|-------------| | `postdeploy` | After a successful deployment (e.g., database migrations) | | `pr-predestroy` | Before a review app is destroyed (e.g., cleanup) | ## Buildpacks The `buildpacks` field maps to migetpacks language support. Heroku-style buildpack URLs are automatically translated to migetpacks language identifiers. ```json { "buildpacks": [ {"url": "heroku/nodejs"}, {"url": "heroku/ruby"} ] } ``` ### Buildpack URL Mapping | Buildpack URL | migetpacks Language | |---------------|-------------------| | `heroku/nodejs` | `nodejs` | | `heroku/ruby` | `ruby` | | `heroku/python` | `python` | | `heroku/go` | `go` | | `heroku/java` | `java` | | `heroku/php` | `php` | | `heroku/scala` | `scala` | | `heroku/clojure` | `clojure` | When multiple buildpacks are specified, the order defines the build sequence. The first buildpack becomes the primary language (determines the runtime base image), and subsequent buildpacks provide additional build stages. The `buildpacks` field in `app.json` is equivalent to setting the `BUILDPACKS` environment variable. If both are provided, the `BUILDPACKS` env var takes precedence. ## Addons The `addons` field declares service dependencies for your application. Each addon can be specified as a simple string or as an object with additional configuration. ### String Format ```json { "addons": [ "heroku-postgresql:hobby-dev", "heroku-redis:premium-0" ] } ``` ### Object Format ```json { "addons": [ { "plan": "heroku-postgresql:standard-0", "as": "PRIMARY_DB" }, { "plan": "mongolab:shared-single-small", "as": "MONGO", "options": { "version": "5.0" } } ] } ``` ### Addon Properties | Property | Description | |----------|-------------| | `plan` | The addon plan identifier (e.g., `heroku-postgresql:hobby-dev`) | | `as` | Attachment alias — overrides the default config var prefix | | `options` | Provider-specific configuration options | String-format addons are normalized to object format in the result JSON with `as` and `options` set to `null`. ## Full Example ```json { "name": "my-rails-app", "env": { "RAILS_ENV": { "value": "production" }, "SECRET_KEY_BASE": { "generator": "secret" }, "DATABASE_URL": { "required": true }, "REDIS_URL": { "required": true }, "RAILS_LOG_TO_STDOUT": { "value": "enabled" } }, "formation": { "web": { "quantity": 2, "size": "standard-1x" }, "worker": { "quantity": 1, "size": "standard-2x" } }, "scripts": { "postdeploy": "bundle exec rails db:migrate" }, "buildpacks": [ {"url": "heroku/nodejs"}, {"url": "heroku/ruby"} ], "addons": [ "heroku-postgresql:hobby-dev", { "plan": "heroku-redis:premium-0", "as": "CACHE" } ] } ``` ## Result JSON Output Values defined in `app.json` appear in the [build result JSON](/reference/result-json) output. Here is how each field maps: ```json { "status": "success", "images": [ {"name": "registry.io/my-app:latest", "ports": ["5000/tcp"]} ], "processes": { "web": "bundle exec puma -C config/puma.rb", "release": "bundle exec rails db:migrate", "workers": [ {"name": "sidekiq", "command": "bundle exec sidekiq"} ] }, "env": { "RAILS_ENV": "production", "SECRET_KEY_BASE": "a1b2c3d4e5f6...", "DATABASE_URL": null, "REDIS_URL": null, "RAILS_LOG_TO_STDOUT": "enabled" }, "formation": [ {"name": "web", "quantity": 2, "size": "standard-1x"}, {"name": "worker", "quantity": 1, "size": "standard-2x"} ], "scripts": [ {"name": "postdeploy", "command": "bundle exec rails db:migrate"} ], "addons": [ {"plan": "heroku-postgresql:hobby-dev", "as": null, "options": null}, {"plan": "heroku-redis:premium-0", "as": "CACHE", "options": null} ], "language": "ruby", "timestamp": "2025-12-18T20:00:00Z" } ``` Variables with `"generator": "secret"` will have their generated value populated in the `env` section of the result JSON. Variables with `"required": true` appear with a `null` value, indicating they must be supplied by the deployment system. --- url: https://migetpacks.com/configuration/build-metadata --- --- title: "Build Metadata" description: "Git provenance and builder facts emitted on every build" --- ## Overview migetpacks records who built an image and from which commit, then exposes that on four surfaces: - The `build` block in the [result JSON](/reference/result-json). - OCI image labels you can read with `docker inspect` without running the image. - An in-image file `/.miget/build.json` (buildpack builds). - `MIGET_*` build args for custom Dockerfile and Compose builds. ## Sources Each field resolves by precedence, highest first: 1. The `build-meta` input envelope (set by the platform). 2. `MIGET_GIT_*` or `SOURCE_VERSION` environment variables, for standalone `docker run` or CI builds. 3. `.git` autodetection of the source directory. Unknown fields are omitted rather than set to empty strings. ## OCI labels | Label | Value | |-------|-------| | `org.opencontainers.image.revision` | Full commit SHA | | `org.opencontainers.image.source` | Repository URL | | `org.opencontainers.image.created` | Build time (ISO 8601) | | `com.miget.git.branch` | Branch | | `com.miget.git.description` | Commit subject | | `com.miget.builder.version` | migetpacks version | ```bash docker inspect my-app:latest --format '{{json .Config.Labels}}' ``` ## In-image file Buildpack builds include `/.miget/build.json`, useful when you prefer a file over environment variables or run a distroless image with no shell: ```bash docker run --rm my-app:latest cat /.miget/build.json ``` ## Custom Dockerfile and Compose builds migetpacks does not modify a Dockerfile it does not own, so it passes the values as build args. Opt in from your Dockerfile: ```dockerfile ARG MIGET_GIT_COMMIT ENV MIGET_GIT_COMMIT=${MIGET_GIT_COMMIT} ``` --- url: https://migetpacks.com/configuration/environment-variables --- --- title: "Environment Variables" description: "Complete reference for all configuration options" --- ## Builder Configuration These environment variables control how migetpacks builds your application. | Variable | Required | Default | Description | |----------|----------|---------|-------------| | `SOURCE_DIR` | No | `/workspace/source` | Source code directory | | `OUTPUT_IMAGE` | Yes | - | Target image name (e.g., `registry.io/app:tag`) | | `LANGUAGE` | No | auto-detected | Programming language override | | `RUN_COMMAND` | No | from Procfile/default | Command to run the application | | `PORT` | No | `5000` | Port the application listens on | | `ARCH` | No | `x86_64` | Target architecture (`x86_64`, `arm64`) | | `PROJECT_PATH` | No | - | Subdirectory within `SOURCE_DIR` for monorepo support | | `DOCKERFILE_PATH` | No | - | Custom Dockerfile path (relative to `PROJECT_PATH` or absolute) | | `COMPOSE_FILE` | No | - | Custom compose file path (auto-detects `compose.yaml`, `compose.yml`, `docker-compose.yaml`, `docker-compose.yml`) | | `BUILDPACKS` | No | auto-detected | Explicit buildpack order (e.g., `ruby,python,nodejs` - first is primary) | | `TAG_LATEST` | No | `false` | Also tag image with `:latest` in addition to primary tag | | `RESULT_FILE` | No | - | Path to write build results JSON (for Shipwright post-build steps) | | `STORAGE_DRIVER` | No | `overlay2` | Docker storage driver override (e.g., `fuse-overlayfs` for nested DinD) | ## Caching Options These variables configure build caching for faster subsequent builds. | Variable | Required | Default | Description | |----------|----------|---------|-------------| | `CACHE_IMAGE` | No | - | Registry image for BuildKit cache | | `CACHE_REGISTRY_INSECURE` | No | `false` | Set to `true` for HTTP registries (e.g., internal cache registry) | | `NO_CACHE` | No | `false` | Force fresh build (`--no-cache`), skips cache-from but still exports to cache-to | | `CACHE_MODE` | No | `min` | BuildKit cache export mode: `min` (final layer only, smaller) or `max` (all layers, better hits) | | `CACHE_FROM` | No | - | Additional read-only cache sources (comma-separated registry refs, skipped when `NO_CACHE=true`) | | `REGISTRY_MIRROR` | No | - | Docker registry mirror URL (e.g., `https://registry.example.io/mirror`) | | `BUILD_CACHE_DIR` | No | - | Shared cache directory for package managers (mount RWX volume here) | ## Docker Hardened Images These variables enable Docker Hardened Images for secure, minimal containers. | Variable | Required | Default | Description | |----------|----------|---------|-------------| | `USE_DHI` | No | `false` | Use Docker Hardened Images (dhi.io) for secure, minimal runtime containers | | `DHI_USERNAME` | No | - | DHI registry username (alternative to mounting docker config) | | `DHI_PASSWORD` | No | - | DHI registry password/token | | `DHI_MIRROR` | No | - | DHI registry mirror URL (e.g., `https://registry.example.io/dhi-io`) | ## External Private Registry Set these variables when your `Dockerfile` pulls a base image from a private registry (any `FROM /...` line). The builder runs `docker login` before invoking the build, so subsequent pulls authenticate. Values may come from the environment directly or from the platform's `BUILD_VARS` JSON envelope. | Variable | Required | Default | Description | |----------|----------|---------|-------------| | `EXTERNAL_REGISTRY_URL` | No | - | Registry host (e.g., `docker.io`, `ghcr.io`, `registry.digitalocean.com`, `.dkr.ecr..amazonaws.com`). No protocol prefix. | | `EXTERNAL_REGISTRY_USERNAME` | No | - | Username (or token name where the provider uses it as the username, e.g., `AWS` for ECR). | | `EXTERNAL_REGISTRY_PASSWORD` | No | - | Password or read-only access token. Read-only tokens are recommended. | All three must be present for the login step to run. Login failure is logged and non-fatal — the FROM-pull error then surfaces during `docker build`, matching the DHI block's behaviour. ### Example ```bash docker docker run --rm \ -v "$(pwd):/workspace/source:ro" \ -v /var/run/docker.sock:/var/run/docker.sock \ -e EXTERNAL_REGISTRY_URL=docker.io \ -e EXTERNAL_REGISTRY_USERNAME=my-user \ -e EXTERNAL_REGISTRY_PASSWORD=$DOCKERHUB_TOKEN \ -e OUTPUT_IMAGE=myapp:latest \ miget/migetpacks:latest-alpine ``` ## Custom Environment Variables For build provenance variables (`MIGET_GIT_COMMIT`, etc.) see [Build Metadata](/configuration/build-metadata). Any environment variable passed to migetpacks that is **not** in the known builder variables list is automatically injected into the generated Dockerfile as an `ENV` statement. This allows you to configure your application's build environment without modifying any configuration files. ### How It Works 1. You pass an environment variable to the migetpacks container (e.g., `-e NODE_OPTIONS="--max-old-space-size=4096"`) 2. The builder checks whether it matches any known builder variable pattern 3. If it does not match, it is added to the generated Dockerfile as an `ENV` instruction 4. The variable becomes available during both the build and runtime stages ```bash docker run --rm \ -v /path/to/app:/workspace/source \ -e OUTPUT_IMAGE=my-app:latest \ -e NODE_OPTIONS="--max-old-space-size=4096" \ -e VITE_API_URL="https://api.example.com" \ miget/migetpacks:latest ``` The generated Dockerfile will include: ```dockerfile # Custom build environment variables ENV NODE_OPTIONS="--max-old-space-size=4096" ENV VITE_API_URL="https://api.example.com" ``` ### Common Use Cases Increase the V8 heap size for large builds that run out of memory: ```bash -e NODE_OPTIONS="--max-old-space-size=4096" ``` Inject variables consumed by Vite, Next.js, or similar frameworks during the build: ```bash -e VITE_API_URL="https://api.example.com" -e NEXT_PUBLIC_ANALYTICS_ID="UA-123456" ``` Provide the master key so Rails can decrypt credentials during asset precompilation: ```bash -e RAILS_MASTER_KEY="abc123def456" ``` Pass arbitrary flags to your build tooling: ```bash -e CARGO_BUILD_JOBS="4" -e MIX_ENV="prod" -e BUNDLE_DEPLOYMENT="true" ``` ### Filtered Variables The following categories of variables are **never** passed through to the Dockerfile: - **Builder config**: `SOURCE_DIR`, `OUTPUT_IMAGE`, `LANGUAGE`, `BUILD_COMMAND`, `RUN_COMMAND`, `PORT`, `ARCH`, `CACHE_IMAGE`, `NO_CACHE`, `CACHE_MODE`, `CACHE_FROM`, `BUILDPACKS`, `USE_DHI`, `REGISTRY_MIRROR`, `RESULT_FILE`, `STORAGE_DRIVER`, etc. - **Language-specific**: Go build flags, Scala/sbt options, Clojure/Leiningen settings - **Credentials**: `AWS_*` variables (never exposed in images) - **Docker/system**: `DOCKER_HOST`, `DOCKER_CONFIG`, `DOCKER_BUILDKIT`, etc. - **Shell/system**: `PATH`, `HOME`, `PWD`, `TERM`, `SHELL`, `USER`, `HOSTNAME`, `LANG`, `LC_*`, `XDG_*`, `SSH_*`, etc. ### Build-arg name requirements App environment variables reach the build through the platform's `BUILD_VARS` envelope and are projected as Docker build args (`--build-arg` flags and generated `ARG` lines). Build args — like shell variables and Dockerfile `ARG`/`ENV` keys — must be **POSIX identifiers** (IEEE Std 1003.1 §8.1): a letter or underscore followed by letters, digits, or underscores, i.e. `^[A-Za-z_][A-Za-z0-9_]*$`. Keys that don't satisfy this rule — for example dotted settings such as `discovery.seed_hosts` (used by Elasticsearch and other JVM apps) — are **skipped from the build-arg projection and logged**. They are still delivered to the running container as ordinary environment variables; they simply cannot be build args, because `ARG discovery.seed_hosts` is not a valid Dockerfile instruction. - **Runtime env** (Compose Specification, permissive): keys are preserved verbatim. - **Build args** (POSIX / Docker `ARG`): only identifier-safe keys are projected; the rest are runtime-only. If you need a non-identifier setting available **at build time**, expose it under an identifier-safe alias (e.g. `ES_DISCOVERY_SEED_HOSTS`) and reference that in your Dockerfile or build script. --- url: https://migetpacks.com/configuration/version-detection --- --- title: "Version Detection" description: "How migetpacks detects language versions" --- migetpacks automatically detects the language version for your project by checking environment variables, version files, and project configuration files in a defined priority order. If no version is found, a sensible default is used. ## Detection Priority by Language Each language checks sources in the order listed below. The first match wins. ### Node.js | Priority | Source | Example | |----------|--------|---------| | 1 | `NODE_VERSION` env var | `NODE_VERSION=20.11.0` | | 2 | `.node-version` file | `20.11.0` | | 3 | `.nvmrc` file | `20` | | 4 | `package.json` `engines.node` | `"node": ">=20"` | | - | Default | `22.16.0` | ### Bun | Priority | Source | Example | |----------|--------|---------| | 1 | `BUN_VERSION` env var | `BUN_VERSION=1.1.0` | | 2 | `.bun-version` file | `1.1.0` | | 3 | `bunfig.toml` `version` field | `version = "1.1.0"` | | - | Default | `1.3.4` | ### Deno | Priority | Source | Example | |----------|--------|---------| | 1 | `DENO_VERSION` env var | `DENO_VERSION=2.0.0` | | 2 | `.deno-version` file | `2.0.0` | | 3 | `.dvmrc` file | `2.0.0` | | 4 | `deno.json` `version` field | `"version": "2.0.0"` | | - | Default | `2.6.1` | ### Ruby | Priority | Source | Example | |----------|--------|---------| | 1 | `RUBY_VERSION` env var | `RUBY_VERSION=3.3.0` | | 2 | `.ruby-version` file | `3.3.0` | | 3 | `Gemfile` ruby directive | `ruby "3.3.0"` | | - | Default | `3.4.4` | ### Python | Priority | Source | Example | |----------|--------|---------| | 1 | `PYTHON_VERSION` env var | `PYTHON_VERSION=3.12.0` | | 2 | `.python-version` file | `3.12.0` | | 3 | `runtime.txt` | `python-3.12.0` | | 4 | `uv.lock` `requires-python` | `requires-python = ">=3.13"` | | 5 | `pyproject.toml` `requires-python` | `requires-python = ">=3.12"` | | - | Default | `3.12.8` | ### Go | Priority | Source | Example | |----------|--------|---------| | 1 | `GO_VERSION` env var | `GO_VERSION=1.22.0` | | 2 | `.go-version` file | `1.22.0` | | 3 | `go.mod` go directive | `go 1.22` | | - | Default | `1.25.0` | ### Rust | Priority | Source | Example | |----------|--------|---------| | 1 | `RUSTUP_TOOLCHAIN` env var | `RUSTUP_TOOLCHAIN=1.80.0` | | 2 | `rust-toolchain` file | `1.80.0` | | 3 | `rust-toolchain.toml` channel | `channel = "1.80.0"` | | - | Default | `1.92.0` | ### PHP | Priority | Source | Example | |----------|--------|---------| | 1 | `PHP_VERSION` env var | `PHP_VERSION=8.3` | | 2 | `.php-version` file | `8.3` | | 3 | `composer.json` `require.php` | `"php": ">=8.2"` | | - | Default | `8.3` | ### Java | Priority | Source | Example | |----------|--------|---------| | 1 | `JAVA_VERSION` env var | `JAVA_VERSION=21` | | 2 | `.java-version` file | `21` | | 3 | `system.properties` | `java.runtime.version=21` | | 4 | `pom.xml` properties | `21` | | - | Default | `21` | ### Kotlin | Priority | Source | Example | |----------|--------|---------| | 1 | `KOTLIN_VERSION` env var | `KOTLIN_VERSION=2.0.0` | | 2 | `.kotlin-version` file | `2.0.0` | | 3 | `gradle.properties` | `kotlin.version=2.0.0` | | 4 | `build.gradle.kts` plugin version | `kotlin("jvm") version "2.0.0"` | | - | Default | `2.1.0` | ### Scala | Priority | Source | Example | |----------|--------|---------| | 1 | `SCALA_VERSION` env var | `SCALA_VERSION=3.4.0` | | 2 | `.scala-version` file | `3.4.0` | | 3 | `build.sbt` scalaVersion | `scalaVersion := "3.4.0"` | | 4 | `project/build.properties` | `scala.version=3.4.0` | | - | Default | `3.5.2` | ### Clojure | Priority | Source | Example | |----------|--------|---------| | 1 | `CLOJURE_VERSION` env var | `CLOJURE_VERSION=1.12.0` | | 2 | `.clojure-version` file | `1.12.0` | | 3 | `project.clj` dependency | `[org.clojure/clojure "1.12.0"]` | | 4 | `deps.edn` dependency | `org.clojure/clojure {:mvn/version "1.12.0"}` | | - | Default | `1.11.1` | ### .NET | Priority | Source | Example | |----------|--------|---------| | 1 | `DOTNET_VERSION` env var | `DOTNET_VERSION=8.0` | | 2 | `global.json` SDK version | `"version": "8.0.100"` | | 3 | `*.csproj` TargetFramework | `net8.0` | | - | Default | `8.0` | ### Elixir | Priority | Source | Example | |----------|--------|---------| | 1 | `ELIXIR_VERSION` env var | `ELIXIR_VERSION=1.17.0` | | 2 | `.elixir-version` file | `1.17.0` | | 3 | `.tool-versions` (asdf) | `elixir 1.17.0` | | - | Default | `1.18.4` | ## Version Normalization migetpacks normalizes version constraints found in project files to Docker-compatible image tags. The following transformations are applied: ### Prefix Removal The `v` prefix is stripped from versions: ```text v20.11.0 -> 20.11.0 ``` ### Semver Range Operators Operators like `>=`, `^`, `~`, `>`, `<`, and `=` are removed, and the base version is extracted: ```text >=18 -> 18 ^20.0.0 -> 20.0 ~20.0.0 -> 20.0 >18 -> 18 ``` For full semver versions with operators, the patch version is dropped: ```text ^20.11.1 -> 20.11 ~3.12.0 -> 3.12 ``` ### Wildcard Patterns The `.x` and `.*` patterns are reduced to the major version: ```text 20.x -> 20 22.* -> 22 ``` ### OR Alternatives When a version string contains `||` separated alternatives, the highest major version is selected: ```text 20.x || 22.x || 24.x -> 24 >=18 || >=20 -> 20 ``` ### Plain Versions Explicit versions without operators are kept as-is, since Docker Hub has tags for all common formats: ```text 20 -> 20 20.11 -> 20.11 20.11.0 -> 20.11.0 3.12.8 -> 3.12.8 ``` --- url: https://migetpacks.com/contributing --- --- title: "Contributing" description: "How to develop, test, and contribute to migetpacks." --- migetpacks is open source and available on [GitHub](https://github.com/migetapp/migetpacks). Contributions are welcome under the [O'Saasy License](https://github.com/migetapp/migetpacks/blob/main/LICENSE). ## Prerequisites - Docker (with BuildKit support) - Bash 4+ - `make` (optional, for build shortcuts) ## Project Structure ```text bin/ ├── detect # Language detection script ├── detect-version # Version detection script ├── build # Main build orchestrator ├── release # Release metadata (Procfile parsing) └── entrypoint.sh # Container entrypoint (starts dockerd) lib/ ├── common.sh # Shared helpers (output formatting, cache IDs) ├── buildpacks.sh # Multi-buildpack support ├── nodejs.sh # Node.js/npm/yarn/pnpm ├── python.sh # Python/pip/uv ├── ruby.sh # Ruby/Bundler/Rails ├── go.sh # Go modules ├── rust.sh # Rust/Cargo ├── java.sh # Java/Maven/Gradle ├── kotlin.sh # Kotlin/Gradle ├── scala.sh # Scala/sbt ├── clojure.sh # Clojure/Leiningen ├── dotnet.sh # .NET/C# ├── php.sh # PHP/Composer/FrankenPHP ├── elixir.sh # Elixir/Phoenix ├── deno.sh # Deno └── bun.sh # Bun examples/ # Working example apps for each language test/ # Test scripts ``` ## Development Commands ```bash # Test the detect scripts ./test/test-detect.sh # Detect language for an example app ./bin/detect examples/nodejs-example # Detect version for an example app LANGUAGE=nodejs ./bin/detect-version examples/nodejs-example # Build the builder container make build # Push to registry make push ``` ## Testing Locally You can test builds locally without pushing to a remote registry: ```bash # Build the migetpacks container docker build -t migetpacks:local . # Run a local build (image stays local, won't push) docker run --rm \ -v /path/to/your/app:/workspace/source \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=my-app:local \ migetpacks:local # Test the built image docker run --rm -p 5000:5000 my-app:local ``` When `OUTPUT_IMAGE` does not contain a registry prefix (e.g., `my-app:local` vs `registry.io/my-app:latest`), the builder uses the local Docker daemon and the image is not pushed. ### Debugging Tips - Check the generated Dockerfile in the build output (`Dockerfile.runtime`) - Inspect the built image: `docker run --rm my-app:local ls -la /app/` - Check environment: `docker run --rm my-app:local env` ## Example Apps Working examples for all supported languages are in `examples/`: | Directory | Language | |-----------|----------| | `nodejs-example` | Node.js | | `deno-example` | Deno | | `bun-example` | Bun | | `python-example` | Python | | `ruby-example` | Ruby | | `go-hello-world` | Go | | `rust-example` | Rust | | `php-hello-world` | PHP | | `java-hello-world` | Java | | `kotlin-example` | Kotlin | | `scala-example` | Scala | | `dotnet-hello-world` | .NET | | `elixir-example` | Elixir | | `dockerfile-example` | Custom Dockerfile | | `compose-example` | Docker Compose | ## Adding a New Language To add support for a new language: 1. Add a detection function in `bin/detect` 2. Add a version detection function in `bin/detect-version` 3. Create a `lib/{language}.sh` with `{language}_get_images()`, `{language}_generate_builder()`, and `{language}_generate_runtime()` functions 4. Wire the new language into `bin/build` 5. Add an example app in `examples/` 6. Add tests in `test/` --- url: https://migetpacks.com/deploying/kubernetes --- --- title: "Kubernetes" description: "Running migetpacks as Kubernetes Jobs" --- ## Overview You can run migetpacks as a Kubernetes Job for one-off or scheduled builds without needing the full Shipwright framework. This approach gives you direct control over the build process while still leveraging Kubernetes for orchestration. ## Kubernetes Job A basic Job that builds and pushes a container image: ```yaml apiVersion: batch/v1 kind: Job metadata: name: build-my-app namespace: builds spec: backoffLimit: 0 # Don't retry failed builds activeDeadlineSeconds: 900 # 15 minute timeout template: spec: restartPolicy: Never containers: - name: build image: miget/migetpacks:latest securityContext: privileged: true # Required for Docker-in-Docker env: - name: OUTPUT_IMAGE value: registry.io/your-org/your-app:latest - name: LANGUAGE value: nodejs volumeMounts: - name: source mountPath: /workspace/source readOnly: true resources: requests: cpu: "1" memory: "2Gi" limits: cpu: "4" memory: "8Gi" volumes: - name: source persistentVolumeClaim: claimName: app-source # PVC with your source code ``` The `privileged: true` security context is required because migetpacks uses Docker-in-Docker (DinD) internally to build images. ## Build Cache with PVC Use a ReadWriteMany (RWX) PersistentVolumeClaim to share package manager caches across builds. This significantly speeds up dependency installation for repeated builds. ```yaml apiVersion: v1 kind: PersistentVolumeClaim metadata: name: build-cache namespace: builds spec: accessModes: - ReadWriteMany # RWX required for concurrent builds resources: requests: storage: 20Gi storageClassName: your-rwx-storage-class # e.g., nfs, cephfs, efs ``` Mount it in your Job: ```yaml volumeMounts: - name: build-cache mountPath: /cache env: - name: BUILD_CACHE_DIR value: /cache volumes: - name: build-cache persistentVolumeClaim: claimName: build-cache ``` The cache stores package manager artifacts per language: | Language | Cache Path | Contents | |----------|-----------|----------| | Node.js | `/cache/npm`, `/cache/yarn`, `/cache/pnpm` | Package tarballs | | Python | `/cache/pip`, `/cache/uv` | Wheel files | | Ruby | `/cache/bundler` | Gem files | | Go | `/cache/go` | Module sources | | Rust | `/cache/cargo` | Crate sources | | Java | `/cache/maven`, `/cache/gradle` | Dependencies | | PHP | `/cache/composer` | Composer packages | ## ConfigMap for Environment Variables Use a ConfigMap to manage build configuration separately from the Job definition: ```yaml apiVersion: v1 kind: ConfigMap metadata: name: build-config namespace: builds data: PORT: "3000" USE_DHI: "true" REGISTRY_MIRROR: "https://registry.example.io/mirror" CACHE_MODE: "max" NODE_OPTIONS: "--max-old-space-size=4096" ``` Reference it in your Job: ```yaml containers: - name: build image: miget/migetpacks:latest envFrom: - configMapRef: name: build-config env: - name: OUTPUT_IMAGE value: registry.io/your-org/your-app:latest ``` ## Secret for Registry Credentials Store registry credentials in a Kubernetes Secret: ```yaml apiVersion: v1 kind: Secret metadata: name: registry-credentials namespace: builds type: kubernetes.io/dockerconfigjson data: .dockerconfigjson: ``` Mount it as a Docker config file: ```yaml containers: - name: build image: miget/migetpacks:latest env: - name: DOCKER_CONFIG value: /docker-config volumeMounts: - name: docker-config mountPath: /docker-config volumes: - name: docker-config secret: secretName: registry-credentials items: - key: .dockerconfigjson path: config.json ``` ## RESULT_FILE with Shared Volume Use an `emptyDir` volume to share build results between the builder and a post-build notification container: ```yaml volumes: - name: results emptyDir: {} ``` Set the `RESULT_FILE` environment variable to write results to this volume: ```yaml env: - name: RESULT_FILE value: /workspace/output/build-result.json volumeMounts: - name: results mountPath: /workspace/output ``` The builder always exits with code 0, writing the build status to the result file. Post-build containers should check the `status` field to determine success or failure. ## Post-Build Notification Use an init container pattern or a sidecar to process build results. Here is an example using a second container that waits for the result file: ```yaml containers: - name: build image: miget/migetpacks:latest # ... build configuration ... - name: notify image: your-rabbitmq-publisher:latest command: ["/bin/sh", "-c"] args: - | echo "Waiting for build result..." while [ ! -f /workspace/output/build-result.json ]; do sleep 2 done echo "Build complete, publishing result..." /app/publish-result /workspace/output/build-result.json env: - name: RABBITMQ_URL valueFrom: secretKeyRef: name: rabbitmq-credentials key: url - name: RABBITMQ_QUEUE value: build-results volumeMounts: - name: results mountPath: /workspace/output ``` ## Full Example A complete Job definition with all components -- source, cache, registry credentials, configuration, and post-build notification: ```yaml apiVersion: batch/v1 kind: Job metadata: name: build-my-app namespace: builds spec: backoffLimit: 0 activeDeadlineSeconds: 900 template: spec: restartPolicy: Never containers: - name: build image: miget/migetpacks:latest securityContext: privileged: true envFrom: - configMapRef: name: build-config env: - name: OUTPUT_IMAGE value: registry.io/your-org/your-app:latest - name: RESULT_FILE value: /workspace/output/build-result.json - name: BUILD_CACHE_DIR value: /cache - name: CACHE_IMAGE value: registry.io/your-org/your-app:cache - name: DOCKER_CONFIG value: /docker-config volumeMounts: - name: source mountPath: /workspace/source readOnly: true - name: build-cache mountPath: /cache - name: results mountPath: /workspace/output - name: docker-config mountPath: /docker-config resources: requests: cpu: "1" memory: "2Gi" limits: cpu: "4" memory: "8Gi" - name: notify image: your-rabbitmq-publisher:latest command: ["/bin/sh", "-c"] args: - | while [ ! -f /workspace/output/build-result.json ]; do sleep 2; done /app/publish-result /workspace/output/build-result.json env: - name: RABBITMQ_URL valueFrom: secretKeyRef: name: rabbitmq-credentials key: url volumeMounts: - name: results mountPath: /workspace/output volumes: - name: source persistentVolumeClaim: claimName: app-source - name: build-cache persistentVolumeClaim: claimName: build-cache - name: results emptyDir: {} - name: docker-config secret: secretName: registry-credentials items: - key: .dockerconfigjson path: config.json ``` --- url: https://migetpacks.com/deploying/miget --- --- title: "Miget" description: "Deploy with migetpacks on the Miget platform" --- ## What is Miget? [Miget](https://miget.com) is the platform that created and maintains migetpacks. When you deploy on Miget, migetpacks is natively integrated. Your code is automatically detected, built, and deployed without any configuration. Miget lets you run unlimited applications on a single compute resource with a fair scheduler that intelligently distributes CPU across your workloads. Each app runs in an isolated microVM with managed databases, custom domains, and unmetered bandwidth included. ## How it works Push your code to Miget and the platform automatically uses migetpacks to: - Detect your language and version from source - Generate an optimized multi-stage Dockerfile - Build a secure, non-root container image - Deploy it across Miget's infrastructure No Dockerfile, no build configuration, and no CI pipeline to maintain. ## Why Miget Run as many apps as you need on shared compute. No per-app billing, no surprise costs. PostgreSQL, MySQL, and Valkey, provisioned in seconds alongside your apps. Every app runs in its own secure VM with CIS and FIPS 140-2 compliance. Deploy to EU and US regions today, with more locations coming soon. ## Getting started Sign up at [miget.com](https://miget.com) and connect your repository. Miget handles the rest. --- url: https://migetpacks.com/deploying/self-hosted-runners --- --- title: "Self-Hosted Runners" description: "Performance tips for CI/CD runners" --- ## Overview Running migetpacks on self-hosted CI/CD runners (GitHub Actions, GitLab, etc.) gives you control over the build environment and enables persistent caching. However, storage architecture and configuration significantly impact build performance. ## Storage Architecture The underlying storage for `/var/lib/docker` (where Docker stores layers and images) directly affects build speed. ### Local NVMe/SSD Best performance. Docker layer operations (unpacking, caching, building) are I/O-intensive and benefit from low-latency local storage. ```text Runner → Docker → ext4 → NVMe/SSD ``` ### Distributed Storage (CephFS, NFS, EFS) Slower due to the I/O path. Docker typically uses a loopback device on top of distributed filesystems, adding overhead: ```text Runner → Docker → ext4 → loopback → CephFS → network → OSD ``` **Symptoms of slow distributed storage:** - High `iowait` during layer unpacking (visible in `top` or `iostat`) - First builds taking 5-10x longer than subsequent builds - Slow `docker pull` operations High iowait during layer unpacking is normal for distributed storage. This is not a migetpacks issue -- it is inherent to Docker's storage driver writing many small files through the network storage path. ### Storage Driver Options migetpacks defaults to `overlay2` but supports alternative storage drivers via the `STORAGE_DRIVER` environment variable: ```bash # For nested Docker-in-Docker on some storage backends STORAGE_DRIVER=fuse-overlayfs ``` Use `fuse-overlayfs` when running nested DinD (e.g., migetpacks inside another Docker container) where `overlay2` is not available. ## Performance Tips ### 1. Pre-pull Base Images Pull commonly used base images during runner initialization to avoid cold-start delays: ```bash # Runner startup script docker pull node:20-slim & docker pull ruby:3.2-slim & docker pull python:3.11-slim & docker pull golang:1.22 & wait ``` ### 2. Use Registry Mirrors Configure a pull-through cache registry (e.g., Harbor proxy cache) to avoid hitting Docker Hub rate limits and reduce pull times: ```bash REGISTRY_MIRROR=https://registry.example.io/mirror ``` The mirror is configured in the Docker daemon's `registry-mirrors` setting inside migetpacks, so all image pulls automatically try the mirror first. ### 3. Persistent Cache Directory Mount a persistent directory for package manager caches that survives runner restarts: ```bash # Create persistent cache directory on the runner mkdir -p /home/runner/migetpacks-cache # Pass it to migetpacks docker run --rm \ -v /home/runner/migetpacks-cache:/cache \ -e BUILD_CACHE_DIR=/cache \ miget/migetpacks:latest ``` ### 4. Docker Layer Cache Persistence Docker layer cache persists in `/var/lib/docker` between builds on the same runner. This means: - Base images are pulled only once - Unchanged layers from previous builds are reused - BuildKit inline cache provides cross-build layer reuse Ensure your runner does not prune Docker images between builds unless disk space is constrained. ### 5. Registry-Based BuildKit Cache Use `CACHE_IMAGE` to store and retrieve BuildKit cache from a registry. This works across runners and survives runner reprovisioning: ```bash CACHE_IMAGE=registry.io/your-org/your-app:cache CACHE_MODE=max # Cache all intermediate layers (better hit rate) ``` ## GitHub Actions Workflow A complete workflow for self-hosted runners with persistent caching: ```yaml name: Build with migetpacks on: push: branches: [main] pull_request: branches: [main] jobs: build: runs-on: self-hosted steps: - uses: actions/checkout@v4 - name: Log into registry uses: docker/login-action@v3 with: registry: your-registry.io username: ${{ secrets.REGISTRY_USERNAME }} password: ${{ secrets.REGISTRY_PASSWORD }} - name: Setup cache directory run: mkdir -p /home/runner/migetpacks-cache - name: Build with migetpacks run: | docker run --rm \ -v ${{ github.workspace }}:/workspace/source:ro \ -v /var/run/docker.sock:/var/run/docker.sock \ -v /home/runner/migetpacks-cache:/cache \ -e OUTPUT_IMAGE=your-registry.io/app:${{ github.sha }} \ -e BUILD_CACHE_DIR=/cache \ -e USE_DHI=true \ -e REGISTRY_MIRROR=https://registry.example.io/mirror \ -e CACHE_IMAGE=your-registry.io/app:cache \ -e CACHE_MODE=max \ miget/migetpacks:latest - name: Tag latest (main only) if: github.ref == 'refs/heads/main' run: | docker tag your-registry.io/app:${{ github.sha }} your-registry.io/app:latest docker push your-registry.io/app:latest ``` ## Build Time Expectations | Scenario | First Build | Subsequent Builds | |----------|------------|-------------------| | Local NVMe, no cache | 30-120s | 10-30s | | Local NVMe, with cache | 20-60s | 5-15s | | CephFS, no cache | 60-300s | 20-60s | | CephFS, with cache | 40-120s | 10-30s | These times vary based on application size, dependency count, and network speed to registries. ## Troubleshooting ### High iowait during builds This is normal for distributed storage. The Docker storage driver writes many small files during layer unpacking. Consider moving `/var/lib/docker` to local SSD if available. ### Docker Hub rate limits Use `REGISTRY_MIRROR` to configure a pull-through cache, or authenticate with Docker Hub to increase rate limits: ```bash docker login -u your-user -p your-token ``` ### Out of disk space Docker layer cache grows over time. Schedule periodic pruning on runners: ```bash # Prune images older than 7 days (run during off-hours) docker image prune -a --filter "until=168h" --force ``` ### Slow first build after runner restart The first build after a restart needs to pull base images and warm the layer cache. Use pre-pulling in your runner startup script to minimize this delay. --- url: https://migetpacks.com/deploying/shipwright --- --- title: "Shipwright" description: "ClusterBuildStrategy for Kubernetes-native builds" --- ## What is Shipwright? [Shipwright](https://shipwright.io) is a Kubernetes-native build framework that provides a declarative API for building container images. It abstracts away the complexity of container builds by defining **BuildStrategies** that encapsulate build tools and processes. migetpacks integrates with Shipwright as a **ClusterBuildStrategy**, allowing you to build application containers directly from source code using Kubernetes-native resources. ## ClusterBuildStrategy The ClusterBuildStrategy defines how migetpacks runs inside the cluster. It specifies the builder image, volumes, and parameters available to builds. ```yaml apiVersion: shipwright.io/v1beta1 kind: ClusterBuildStrategy metadata: name: migetpacks spec: volumes: - name: results emptyDir: {} - name: build-cache persistentVolumeClaim: claimName: build-cache # RWX PVC shared across builds steps: - name: build image: miget/migetpacks:latest securityContext: privileged: true # Required for Docker-in-Docker volumeMounts: - name: results mountPath: /workspace/output - name: build-cache mountPath: /cache env: - name: SOURCE_DIR value: $(params.shp-source-root) - name: OUTPUT_IMAGE value: $(params.shp-output-image) - name: RESULT_FILE value: /workspace/output/build-result.json - name: BUILD_CACHE_DIR value: /cache resources: requests: cpu: "500m" memory: "1Gi" limits: cpu: "2" memory: "4Gi" ``` The `privileged: true` security context is required because migetpacks uses Docker-in-Docker (DinD) to build container images. ## Volumes migetpacks uses several volume types in Shipwright builds: | Volume | Type | Purpose | |--------|------|---------| | Source | Shipwright-managed | Source code from Git repository | | Results | `emptyDir` | Build result JSON for post-build steps | | Build Cache | PVC (RWX) | Shared package manager cache across builds | ## Environment Variables Pass environment variables to customize the build behavior. These can be set directly in the BuildStrategy or passed through the Build resource. ```yaml env: - name: LANGUAGE value: ruby - name: PORT value: "3000" - name: USE_DHI value: "true" - name: REGISTRY_MIRROR value: https://registry.example.io/mirror - name: CACHE_IMAGE value: registry.io/myapp:cache ``` Any environment variable not in the known builder variables list is automatically injected into the generated Dockerfile as an `ENV` statement. This is useful for passing build-time configuration like `NODE_OPTIONS` or `RAILS_MASTER_KEY`. ## RESULT_FILE for Post-Build Steps When `RESULT_FILE` is set, migetpacks writes a JSON file containing build results. This enables post-build steps (like deployment notifications) to access build metadata. The builder always exits with code 0 to ensure post-build steps execute. Check the `status` field in the result JSON to determine if the build succeeded or failed. ```yaml volumes: - name: results emptyDir: {} steps: - name: build image: miget/migetpacks:latest volumeMounts: - name: results mountPath: /workspace/output env: - name: RESULT_FILE value: /workspace/output/build-result.json - name: notify image: your-rabbitmq-publisher:latest volumeMounts: - name: results mountPath: /workspace/output # Reads /workspace/output/build-result.json and publishes to RabbitMQ ``` ## Build Resource The Build resource defines what to build and where to push the result. ```yaml apiVersion: shipwright.io/v1beta1 kind: Build metadata: name: my-app namespace: default spec: source: type: Git git: url: https://github.com/your-org/your-app.git revision: main contextDir: . # Or a subdirectory for monorepos strategy: name: migetpacks kind: ClusterBuildStrategy output: image: registry.io/your-org/your-app:latest credentials: name: registry-credentials # Secret with .dockerconfigjson env: - name: USE_DHI value: "true" - name: PORT value: "3000" timeout: 10m ``` ## BuildRun Resource A BuildRun triggers an actual build execution. ```yaml apiVersion: shipwright.io/v1beta1 kind: BuildRun metadata: name: my-app-run-1 namespace: default spec: build: name: my-app env: - name: NO_CACHE value: "true" # Force fresh build for this run serviceAccount: name: build-sa timeout: 15m ``` You can also create a BuildRun that overrides the output image: ```yaml apiVersion: shipwright.io/v1beta1 kind: BuildRun metadata: generateName: my-app-run- namespace: default spec: build: name: my-app output: image: registry.io/your-org/your-app:$(git-sha) credentials: name: registry-credentials ``` ## Full Example with Cache and Results This complete example shows a ClusterBuildStrategy with build caching, registry mirror, and a post-build notification step: ```yaml apiVersion: shipwright.io/v1beta1 kind: ClusterBuildStrategy metadata: name: migetpacks-full spec: volumes: - name: results emptyDir: {} - name: build-cache persistentVolumeClaim: claimName: migetpacks-cache # RWX PVC steps: - name: build image: miget/migetpacks:latest securityContext: privileged: true volumeMounts: - name: results mountPath: /workspace/output - name: build-cache mountPath: /cache env: - name: SOURCE_DIR value: $(params.shp-source-root) - name: OUTPUT_IMAGE value: $(params.shp-output-image) - name: RESULT_FILE value: /workspace/output/build-result.json - name: BUILD_CACHE_DIR value: /cache - name: REGISTRY_MIRROR value: https://registry.example.io/mirror - name: USE_DHI value: "true" - name: CACHE_IMAGE value: $(params.shp-output-image)-cache - name: CACHE_MODE value: max resources: requests: cpu: "1" memory: "2Gi" limits: cpu: "4" memory: "8Gi" - name: notify image: your-notification-service:latest volumeMounts: - name: results mountPath: /workspace/output env: - name: RESULT_PATH value: /workspace/output/build-result.json - name: RABBITMQ_URL valueFrom: secretKeyRef: name: rabbitmq-credentials key: url ``` --- url: https://migetpacks.com/guides/build-caching --- --- title: "Build Caching" description: "Speed up builds with registry-based caching, shared package manager caches, and optimized Docker layer ordering." --- ## Overview migetpacks supports three complementary caching strategies that work together to minimize build times: 1. **Registry-based BuildKit cache** (`CACHE_IMAGE`) - stores build layers in a container registry 2. **Shared package manager cache** (`BUILD_CACHE_DIR`) - persists dependency downloads on a shared volume 3. **Docker layer caching** - optimized COPY order in generated Dockerfiles ## Registry-Based Cache (CACHE_IMAGE) Store BuildKit cache layers in a container registry for sharing across builds, runners, and CI environments. ```bash docker run --rm \ -v /path/to/app:/workspace/source:ro \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=registry.io/my-app:latest \ -e CACHE_IMAGE=registry.io/my-app:buildcache \ miget/migetpacks:latest ``` ### Cache Mode Control how much data is stored in the registry cache with `CACHE_MODE`: Caches only the final layer. Produces smaller cache images but fewer cache hits on intermediate layers. ```bash -e CACHE_MODE=min ``` Caches all intermediate layers. Larger cache images but better cache hit rates, especially when only source code changes. ```bash -e CACHE_MODE=max ``` ### Additional Cache Sources (CACHE_FROM) Read from multiple cache sources (comma-separated). Useful for sharing cache across branches or teams: ```bash -e CACHE_IMAGE=registry.io/my-app:cache-main \ -e CACHE_FROM=registry.io/my-app:cache-dev,registry.io/my-app:cache-feature ``` `CACHE_FROM` sources are read-only. The build always writes to `CACHE_IMAGE` but can read from both `CACHE_IMAGE` and all `CACHE_FROM` entries. ### Force Fresh Build (NO_CACHE) Skip reading from cache while still exporting to the cache registry: ```bash -e NO_CACHE=true ``` When `NO_CACHE=true`: - `--no-cache` is passed to the build - `CACHE_FROM` sources are skipped - Cache is still exported to `CACHE_IMAGE` (so subsequent builds can use it) ### Insecure Registry (CACHE_REGISTRY_INSECURE) For internal HTTP registries (e.g., a local cache registry), enable insecure mode: ```bash -e CACHE_IMAGE=http-registry.internal:5000/my-app:buildcache \ -e CACHE_REGISTRY_INSECURE=true ``` ## Shared Package Manager Cache (BUILD_CACHE_DIR) Mount a persistent volume to cache package manager downloads across builds. This is especially effective on self-hosted runners or Kubernetes environments with ReadWriteMany (RWX) PersistentVolumeClaims. ```bash docker run --rm \ -v /path/to/app:/workspace/source:ro \ -v /var/run/docker.sock:/var/run/docker.sock \ -v /persistent-cache:/cache \ -e OUTPUT_IMAGE=registry.io/my-app:latest \ -e BUILD_CACHE_DIR=/cache \ miget/migetpacks:latest ``` ### Cache Directories by Language | Language | Cache Location | What's Cached | |----------|---------------|---------------| | Node.js | `/cache/npm`, `/cache/yarn`, `/cache/pnpm` | Package tarballs | | Python | `/cache/pip`, `/cache/uv` | Wheel files | | Ruby | `/cache/bundler` | Gem files | | Go | `/cache/go` | Module sources | | Rust | `/cache/cargo` | Crate sources | | Java | `/cache/maven`, `/cache/gradle` | Dependencies | | PHP | `/cache/composer` | Composer packages | BuildKit cache mounts (`--mount=type=cache,sharing=shared`) are automatically added to generated Dockerfiles for all supported languages. The `sharing=shared` mode allows concurrent builds to share cache without locking. ### Kubernetes PVC Example ```yaml spec: volumes: - name: build-cache persistentVolumeClaim: claimName: build-cache # RWX PVC buildSteps: - name: build image: miget/migetpacks:latest volumeMounts: - name: build-cache mountPath: /cache env: - name: BUILD_CACHE_DIR value: /cache ``` ## Docker Layer Caching All generated Dockerfiles use optimized COPY ordering to maximize Docker layer cache hits. This requires no configuration and works automatically. ### How It Works ```dockerfile # 1. Copy dependency files first (changes rarely) COPY package.json package-lock.json ./ # 2. Install dependencies (CACHED if lockfiles unchanged) RUN npm ci # 3. Copy source code (changes frequently) COPY . . # 4. Build and cleanup RUN npm run build && npm prune --production ``` ### Cache Behavior | Scenario | Dependency Install | Build Step | |----------|-------------------|------------| | First build | Runs | Runs | | Source code change only | **CACHED** | Runs | | Lockfile change | Runs | Runs | | No changes | **CACHED** | **CACHED** | This optimization is applied for all languages: - **Node.js**: `package.json` + lockfile copied first - **Python**: `requirements.txt` / `pyproject.toml` copied first - **Ruby**: `Gemfile` + `Gemfile.lock` copied first - **Go**: `go.mod` + `go.sum` copied first - **Rust**: `Cargo.toml` + `Cargo.lock` copied first - **Java**: `pom.xml` / `build.gradle` copied first - **.NET**: `*.csproj` / `*.sln` copied first ## Combining Strategies For optimal performance, use all three strategies together: ```bash docker run --rm \ -v /path/to/app:/workspace/source:ro \ -v /var/run/docker.sock:/var/run/docker.sock \ -v /persistent-cache:/cache \ -e OUTPUT_IMAGE=registry.io/my-app:latest \ -e CACHE_IMAGE=registry.io/my-app:buildcache \ -e CACHE_MODE=max \ -e BUILD_CACHE_DIR=/cache \ -e REGISTRY_MIRROR=https://registry.example.io/mirror \ miget/migetpacks:latest ``` This gives you: - **Registry cache**: BuildKit layers cached remotely (works across machines) - **Package cache**: Dependency downloads cached locally (fastest for reinstalls) - **Layer cache**: Docker layer ordering optimized (fastest for source-only changes) - **Registry mirror**: Base image pulls from local cache (fastest for image downloads) ## Registry Mirror Use `REGISTRY_MIRROR` to configure a pull-through cache for Docker Hub base images: ```bash -e REGISTRY_MIRROR=https://registry.example.io/mirror ``` The mirror is configured in the Docker daemon's `registry-mirrors` setting, so all image pulls automatically try the mirror first before falling back to Docker Hub. --- url: https://migetpacks.com/guides/custom-dockerfile --- --- title: "Custom Dockerfile" description: "Use your own Dockerfile instead of the auto-generated one for projects with specific build requirements." --- ## Overview migetpacks prioritizes its optimized multi-stage builds for known languages. A `Dockerfile` in your project is only used as a **fallback** when no supported language is detected. To force migetpacks to use your Dockerfile, set `LANGUAGE=dockerfile` or `DOCKERFILE_PATH`. ## Detection Priority If `DOCKERFILE_PATH` is set, or `LANGUAGE=dockerfile` is specified, your Dockerfile is used immediately. migetpacks checks for supported languages (Ruby, Python, Go, Node.js, etc.) and generates an optimized build. Only if no supported language is detected, migetpacks falls back to using `Dockerfile` in your project root. This priority order ensures you get migetpacks' optimized builds by default. If your project has both `package.json` and `Dockerfile`, migetpacks will detect Node.js and generate its own Dockerfile. Use `LANGUAGE=dockerfile` to override this behavior. ## Using DOCKERFILE_PATH Point to a specific Dockerfile when it is not in the default location: ```bash docker run --rm \ -v /path/to/app:/workspace/source:ro \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=registry.io/my-app:latest \ -e DOCKERFILE_PATH=docker/Dockerfile.prod \ miget/migetpacks:latest ``` `DOCKERFILE_PATH` is relative to the project root (or `PROJECT_PATH` if set). You can also use an absolute path. ## Examples ### Standard Dockerfile in Root If your project has a `Dockerfile` in the root directory, no extra configuration is needed: ```text my-app/ ├── Dockerfile # Used automatically ├── src/ ├── package.json └── ... ``` ```bash docker run --rm \ -v /path/to/my-app:/workspace/source:ro \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=registry.io/my-app:latest \ miget/migetpacks:latest ``` ### Multiple Dockerfiles Projects with environment-specific Dockerfiles: ```text my-app/ ├── docker/ │ ├── Dockerfile.dev │ ├── Dockerfile.prod │ └── Dockerfile.test ├── src/ └── ... ``` ```bash Production docker run --rm \ -v /path/to/my-app:/workspace/source:ro \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=registry.io/my-app:latest \ -e DOCKERFILE_PATH=docker/Dockerfile.prod \ miget/migetpacks:latest ``` ```bash Development docker run --rm \ -v /path/to/my-app:/workspace/source:ro \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=my-app:dev \ -e DOCKERFILE_PATH=docker/Dockerfile.dev \ miget/migetpacks:latest ``` ### Monorepo with Custom Dockerfile Combine `PROJECT_PATH` and `DOCKERFILE_PATH`: ```bash docker run --rm \ -v /path/to/monorepo:/workspace/source:ro \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=registry.io/my-service:latest \ -e PROJECT_PATH=services/api \ -e DOCKERFILE_PATH=Dockerfile.production \ miget/migetpacks:latest ``` This looks for the Dockerfile at `services/api/Dockerfile.production`. ## Use Cases ### Custom Base Images When you need a specific base image not supported by migetpacks: ```dockerfile FROM my-internal-registry.io/custom-base:latest WORKDIR /app COPY . . RUN make build EXPOSE 8080 CMD ["./my-app"] ``` If the base image lives in a private registry, set [`EXTERNAL_REGISTRY_URL` / `EXTERNAL_REGISTRY_USERNAME` / `EXTERNAL_REGISTRY_PASSWORD`](../configuration/environment-variables#external-private-registry) on the build so the builder can authenticate before pulling. ### Complex Multi-Stage Builds When your build process requires custom stages: ```dockerfile FROM node:20 AS frontend WORKDIR /frontend COPY frontend/package*.json ./ RUN npm ci COPY frontend/ . RUN npm run build FROM golang:1.22 AS backend WORKDIR /backend COPY go.* ./ RUN go mod download COPY . . RUN CGO_ENABLED=0 go build -o /app FROM gcr.io/distroless/static-debian12 COPY --from=backend /app /app COPY --from=frontend /frontend/dist /static EXPOSE 8080 CMD ["/app"] ``` ### Specific Optimization Needs When you need fine-grained control over layer ordering, build arguments, or caching: ```dockerfile FROM python:3.12-slim AS builder ARG PIP_INDEX_URL WORKDIR /app # Install system dependencies RUN apt-get update && apt-get install -y --no-install-recommends \ libpq-dev gcc && rm -rf /var/lib/apt/lists/* # Layer-cached dependency install COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . FROM python:3.12-slim COPY --from=builder /usr/local/lib/python3.12/site-packages /usr/local/lib/python3.12/site-packages COPY --from=builder /app /app WORKDIR /app EXPOSE 5000 CMD ["python", "app.py"] ``` ## Build Caching with Custom Dockerfiles Registry-based caching (`CACHE_IMAGE`) works with custom Dockerfiles: ```bash docker run --rm \ -v /path/to/app:/workspace/source:ro \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=registry.io/my-app:latest \ -e DOCKERFILE_PATH=Dockerfile \ -e CACHE_IMAGE=registry.io/my-app:buildcache \ -e CACHE_MODE=max \ miget/migetpacks:latest ``` `BUILD_CACHE_DIR` and language-specific BuildKit cache mounts are only available in auto-generated Dockerfiles. If you use a custom Dockerfile, you must manage cache mounts yourself. ## What migetpacks Still Provides Even when using a custom Dockerfile, migetpacks handles: - Building the image with BuildKit - Pushing to the target registry (`OUTPUT_IMAGE`) - Registry-based caching (`CACHE_IMAGE`) - Architecture targeting (`ARCH`) - Registry mirror configuration (`REGISTRY_MIRROR`) - Result file generation (`RESULT_FILE`) - Port detection from the built image --- url: https://migetpacks.com/guides/docker-compose --- --- title: "Docker Compose" description: "Build multi-service applications from Docker Compose files, with each service pushed as a separate image." --- ## Overview migetpacks supports Docker Compose files for building multi-service applications. When a compose file is detected, all services with a `build` section are built and pushed to the registry as separate images. ## Detection Priority migetpacks looks for compose files in the following order: If set, this path is used directly. The modern default compose file name. Alternative YAML extension. Legacy Docker Compose file name. Legacy alternative YAML extension. If none of these files are found, migetpacks falls back to standard language detection and single-image build. ## Image Naming Each service's image is named by appending the service name to `OUTPUT_IMAGE`: ```text OUTPUT_IMAGE-{service_name} ``` For example, with `OUTPUT_IMAGE=registry.io/myapp:latest`: | Service | Image Name | |---------|-----------| | api | `registry.io/myapp-api:latest` | | web | `registry.io/myapp-web:latest` | | worker | `registry.io/myapp-worker:latest` | ## Example ### compose.yaml ```yaml services: api: build: context: ./api dockerfile: Dockerfile ports: - "5000:5000" web: build: context: ./web dockerfile: Dockerfile ports: - "3000:3000" worker: build: ./worker # shorthand syntax ``` ### Project Structure ```text my-project/ ├── compose.yaml ├── api/ │ ├── Dockerfile │ ├── main.go │ └── go.mod ├── web/ │ ├── Dockerfile │ ├── package.json │ └── src/ └── worker/ ├── Dockerfile ├── requirements.txt └── worker.py ``` ### Build Command ```bash docker run --rm \ -v /path/to/my-project:/workspace/source:ro \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=registry.io/myapp:latest \ miget/migetpacks:latest ``` ### Build Output The build produces three images: - `registry.io/myapp-api:latest` - `registry.io/myapp-web:latest` - `registry.io/myapp-worker:latest` ## Custom Compose File Use `COMPOSE_FILE` to specify a non-standard compose file: ```bash Production Compose docker run --rm \ -v /path/to/project:/workspace/source:ro \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=registry.io/myapp:latest \ -e COMPOSE_FILE=docker-compose.prod.yaml \ miget/migetpacks:latest ``` ```bash Staging Compose docker run --rm \ -v /path/to/project:/workspace/source:ro \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=registry.io/myapp:staging \ -e COMPOSE_FILE=docker-compose.staging.yaml \ miget/migetpacks:latest ``` ## Compose File Syntax migetpacks supports both the full and shorthand `build` syntax: ```yaml services: api: build: context: ./api dockerfile: Dockerfile args: NODE_ENV: production ``` ```yaml services: api: build: ./api ``` This is equivalent to setting `context: ./api` with the default `Dockerfile`. ## Services Without Build Sections Services without a `build` section (e.g., database services using pre-built images) are skipped: ```yaml services: api: build: ./api # Built and pushed web: build: ./web # Built and pushed postgres: image: postgres:16 # Skipped (no build section) redis: image: redis:7 # Skipped (no build section) ``` Only `api` and `web` are built; `postgres` and `redis` are ignored. ## Build Caching with Compose Registry-based caching works with compose builds. Each service gets its own cache image: ```bash docker run --rm \ -v /path/to/project:/workspace/source:ro \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=registry.io/myapp:latest \ -e CACHE_IMAGE=registry.io/myapp:buildcache \ miget/migetpacks:latest ``` ## CI/CD Example ### GitHub Actions ```yaml name: Build Multi-Service App on: push: branches: [main] jobs: build: runs-on: self-hosted steps: - uses: actions/checkout@v4 - name: Log into registry uses: docker/login-action@v3 with: registry: your-registry.io username: ${{ secrets.REGISTRY_USERNAME }} password: ${{ secrets.REGISTRY_PASSWORD }} - name: Build all services run: | docker run --rm \ -v ${{ github.workspace }}:/workspace/source:ro \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=your-registry.io/myapp:${{ github.sha }} \ miget/migetpacks:latest ``` This produces: - `your-registry.io/myapp-api:{sha}` - `your-registry.io/myapp-web:{sha}` - `your-registry.io/myapp-worker:{sha}` --- url: https://migetpacks.com/guides/github-actions --- --- title: "GitHub Actions" description: "Use migetpacks in GitHub Actions workflows for automated container builds on self-hosted and GitHub-hosted runners." --- ## Overview migetpacks integrates with GitHub Actions to build and push container images as part of your CI/CD pipeline. It works with both self-hosted runners (recommended for performance) and GitHub-hosted runners. ## Self-Hosted Runner Self-hosted runners offer the best performance because Docker layer caches and package manager caches persist between builds. ```yaml name: Build with migetpacks on: push: branches: [main] jobs: build: runs-on: self-hosted steps: - uses: actions/checkout@v4 - name: Log into registry uses: docker/login-action@v3 with: registry: your-registry.io username: ${{ secrets.REGISTRY_USERNAME }} password: ${{ secrets.REGISTRY_PASSWORD }} - name: Setup cache directory run: mkdir -p /home/runner/migetpacks-cache - name: Build with migetpacks run: | docker run --rm \ -v ${{ github.workspace }}:/workspace/source:ro \ -v /var/run/docker.sock:/var/run/docker.sock \ -v /home/runner/migetpacks-cache:/cache \ -e OUTPUT_IMAGE=your-registry.io/app:${{ github.sha }} \ -e BUILD_CACHE_DIR=/cache \ -e REGISTRY_MIRROR=https://registry.example.io/mirror \ -e USE_DHI=true \ miget/migetpacks:latest ``` The persistent cache directory (`/home/runner/migetpacks-cache`) survives runner restarts and dramatically speeds up dependency installation on subsequent builds. ## GitHub-Hosted Runner GitHub-hosted runners do not persist Docker layer caches between runs. Use `CACHE_IMAGE` for registry-based caching to speed up builds. ```yaml name: Build with migetpacks on: push: branches: [main] pull_request: jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Log into registry uses: docker/login-action@v3 with: registry: your-registry.io username: ${{ secrets.REGISTRY_USERNAME }} password: ${{ secrets.REGISTRY_PASSWORD }} - name: Build with migetpacks run: | docker run --rm \ -v ${{ github.workspace }}:/workspace/source:ro \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=your-registry.io/app:${{ github.sha }} \ -e CACHE_IMAGE=your-registry.io/app:buildcache \ -e CACHE_MODE=max \ miget/migetpacks:latest ``` ## Configuration Options ### Registry Login Always authenticate before running migetpacks if you are pushing to a private registry: ```yaml Docker Hub - name: Log into Docker Hub uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} ``` ```yaml GitHub Container Registry - name: Log into GHCR uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} ``` ```yaml AWS ECR - name: Log into ECR uses: docker/login-action@v3 with: registry: 123456789.dkr.ecr.us-east-1.amazonaws.com username: ${{ secrets.AWS_ACCESS_KEY_ID }} password: ${{ secrets.AWS_SECRET_ACCESS_KEY }} ``` ### Build Cache Directory `BUILD_CACHE_DIR` enables shared package manager caching. On self-hosted runners, point this to a persistent directory: ```yaml - name: Setup cache directory run: mkdir -p /home/runner/migetpacks-cache - name: Build with migetpacks run: | docker run --rm \ -v /home/runner/migetpacks-cache:/cache \ -e BUILD_CACHE_DIR=/cache \ ... ``` ### Registry Mirror `REGISTRY_MIRROR` configures a pull-through cache for Docker Hub images, reducing image pull times: ```yaml -e REGISTRY_MIRROR=https://your-mirror.example.com ``` ### Custom Environment Variables Pass build-time environment variables directly: ```yaml - name: Build with migetpacks run: | docker run --rm \ -v ${{ github.workspace }}:/workspace/source:ro \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=your-registry.io/app:${{ github.sha }} \ -e NODE_OPTIONS="--max-old-space-size=4096" \ -e NEXT_PUBLIC_API_URL="${{ vars.API_URL }}" \ -e RAILS_MASTER_KEY="${{ secrets.RAILS_MASTER_KEY }}" \ miget/migetpacks:latest ``` Secrets like `RAILS_MASTER_KEY` are injected as build-time environment variables and will be present in the generated Dockerfile. They are visible in the final image's environment. Use runtime secrets injection for sensitive values in production. ## Full Workflow Example This complete workflow builds on push to main, tags with both commit SHA and `latest`, and uses all available caching strategies: ```yaml name: Build and Push on: push: branches: [main] jobs: build: runs-on: self-hosted steps: - uses: actions/checkout@v4 - name: Log into registry uses: docker/login-action@v3 with: registry: your-registry.io username: ${{ secrets.REGISTRY_USERNAME }} password: ${{ secrets.REGISTRY_PASSWORD }} - name: Setup persistent cache run: mkdir -p /home/runner/migetpacks-cache - name: Build with migetpacks run: | docker run --rm \ -v ${{ github.workspace }}:/workspace/source:ro \ -v /var/run/docker.sock:/var/run/docker.sock \ -v /home/runner/migetpacks-cache:/cache \ -e OUTPUT_IMAGE=your-registry.io/app:${{ github.sha }} \ -e BUILD_CACHE_DIR=/cache \ -e CACHE_IMAGE=your-registry.io/app:buildcache \ -e CACHE_MODE=max \ -e REGISTRY_MIRROR=https://registry.example.io/mirror \ -e USE_DHI=true \ miget/migetpacks:latest ``` ## Performance Tips - **Persistent cache**: Use `BUILD_CACHE_DIR` pointed at a local directory - **Pre-pull images**: Pull common base images during runner initialization - **Registry mirror**: Use `REGISTRY_MIRROR` for Docker Hub images - **Local Docker cache**: Docker layer cache persists in `/var/lib/docker` - **NVMe storage**: Use local SSD/NVMe for `/var/lib/docker` instead of network storage - **Registry cache**: Use `CACHE_IMAGE` with `CACHE_MODE=max` for best cache hits - **Additional sources**: Use `CACHE_FROM` to read from multiple cache images - **Smaller images**: Use `USE_DHI=true` for distroless runtime images --- url: https://migetpacks.com/guides/hardened-images --- --- title: "Docker Hardened Images" description: "Build secure, minimal containers using Docker Hardened Images (DHI) with distroless runtime environments." --- ## Overview Docker Hardened Images (DHI) from `dhi.io` provide secure, CVE-free base images with minimal attack surfaces. When enabled, migetpacks uses DHI images for both the build stage (`-dev` variants with shell) and the runtime stage (distroless, no shell). ## Enabling DHI Set `USE_DHI=true` to enable Docker Hardened Images: ```bash docker run --rm \ -v /path/to/app:/workspace/source:ro \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=registry.io/my-app:latest \ -e USE_DHI=true \ miget/migetpacks:latest ``` ## Registry Authentication The `dhi.io` registry requires authentication. How you provide credentials depends on your environment. ### CI/CD Environments (GitHub Actions, GitLab CI) In CI/CD pipelines, credentials are typically available via secrets. The easiest approach is to pass them as environment variables: ```bash docker run --rm \ -v /path/to/app:/workspace/source:ro \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=my-app:latest \ -e USE_DHI=true \ -e DHI_USERNAME=$DHI_USERNAME \ -e DHI_PASSWORD=$DHI_PASSWORD \ miget/migetpacks:latest ``` Or create a Docker config file with credentials and mount it: ```yaml # GitHub Actions example - name: Create DHI auth config run: | mkdir -p /tmp/docker-config echo '{"auths":{"dhi.io":{"auth":"'$(echo -n "${{ secrets.DHI_USERNAME }}:${{ secrets.DHI_PASSWORD }}" | base64)'"}}}' > /tmp/docker-config/config.json - name: Build with DHI run: | docker run --rm \ -v $(pwd):/workspace/source:ro \ -v /var/run/docker.sock:/var/run/docker.sock \ -v /tmp/docker-config/config.json:/root/.docker/config.json:ro \ -e OUTPUT_IMAGE=my-app:latest \ -e USE_DHI=true \ miget/migetpacks:latest ``` ### Local Development (Docker Desktop) Docker Desktop on Mac and Windows stores credentials in the system keychain (`credsStore: desktop`), not in `~/.docker/config.json`. When you run `docker login dhi.io`, the credentials are stored in the keychain, but the migetpacks container cannot access them. Extract the credentials from the keychain and pass them as environment variables: ```bash # Extract credentials from Docker Desktop keychain DHI_USERNAME=$(echo "dhi.io" | docker-credential-desktop get | jq -r '.Username') DHI_PASSWORD=$(echo "dhi.io" | docker-credential-desktop get | jq -r '.Secret') # Build with DHI docker run --rm \ -v "$(pwd):/workspace/source:ro" \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=my-app:local \ -e ARCH=arm64 \ -e USE_DHI=true \ -e DHI_USERNAME="$DHI_USERNAME" \ -e DHI_PASSWORD="$DHI_PASSWORD" \ miget/migetpacks:latest ``` Add this to your shell profile (`.bashrc` or `.zshrc`) for convenience: ```bash alias migetpacks-dhi='DHI_USERNAME=$(echo "dhi.io" | docker-credential-desktop get | jq -r ".Username") DHI_PASSWORD=$(echo "dhi.io" | docker-credential-desktop get | jq -r ".Secret") docker run --rm -v "$(pwd):/workspace/source:ro" -v /var/run/docker.sock:/var/run/docker.sock -e USE_DHI=true -e DHI_USERNAME="$DHI_USERNAME" -e DHI_PASSWORD="$DHI_PASSWORD"' ``` Then use: `migetpacks-dhi -e OUTPUT_IMAGE=my-app:local miget/migetpacks:latest` ### Linux On Linux, credentials are typically stored directly in `~/.docker/config.json` after running `docker login dhi.io`. Mount this file: ```bash docker run --rm \ -v "$(pwd):/workspace/source:ro" \ -v /var/run/docker.sock:/var/run/docker.sock \ -v ~/.docker/config.json:/root/.docker/config.json:ro \ -e OUTPUT_IMAGE=my-app:local \ -e USE_DHI=true \ miget/migetpacks:latest ``` If you're using a credential helper (like `pass` or `secretservice`), extract credentials and pass them as environment variables: ```bash # For pass credential helper DHI_USERNAME=$(echo "dhi.io" | docker-credential-pass get | jq -r '.Username') DHI_PASSWORD=$(echo "dhi.io" | docker-credential-pass get | jq -r '.Secret') # For secretservice credential helper DHI_USERNAME=$(echo "dhi.io" | docker-credential-secretservice get | jq -r '.Username') DHI_PASSWORD=$(echo "dhi.io" | docker-credential-secretservice get | jq -r '.Secret') ``` ### Kubernetes/Shipwright When running migetpacks in Kubernetes via Shipwright, use an `imagePullSecret` containing your DHI credentials: ```yaml apiVersion: v1 kind: Secret metadata: name: dhi-credentials type: kubernetes.io/dockerconfigjson data: .dockerconfigjson: ``` Then reference it in your ClusterBuildStrategy or Build resource. ## Image Variants The `-dev` variants include: - Shell (`/bin/sh`) - Package manager (`apt-get`) - Build tools (compilers, headers) Used during the build stage for installing dependencies and compiling code. The runtime variants are distroless: - No shell - No package manager - No unnecessary binaries - Minimal attack surface - Non-root user: `nonroot` Used for the final container that runs in production. ## Supported Languages | Language | Build Image | Runtime Image | Notes | |----------|-------------|---------------|-------| | Node.js | `dhi.io/node:{version}-dev` | `dhi.io/node:{version}` | Distroless runtime | | Deno | `dhi.io/deno:{version}-dev` | `dhi.io/deno:{version}` | Requires 2.6.4+ | | Bun | `dhi.io/bun:{version}-dev` | `dhi.io/bun:{version}` | Requires 1.3.6+ | | Ruby | `dhi.io/ruby:{version}-dev` | `dhi.io/ruby:{version}` | Libraries copied from builder | | Python | `dhi.io/python:{version}-dev` | `dhi.io/python:{version}` | Distroless runtime | | Go | `dhi.io/golang:{version}-dev` | `dhi.io/golang:{version}` | Requires 1.22+ | | Rust | `dhi.io/rust:{version}-dev` | `dhi.io/rust:{version}` | Distroless runtime | | Java | `dhi.io/eclipse-temurin:{version}-jdk-dev` | `dhi.io/eclipse-temurin:{version}` | Maven/Gradle installed in builder | | Kotlin | `dhi.io/eclipse-temurin:{version}-jdk-dev` | `dhi.io/eclipse-temurin:{version}` | Reads system.properties | | Scala | `sbtscala/scala-sbt:...` (official) | `dhi.io/eclipse-temurin:{java}` | Official build, DHI runtime | | Clojure | `clojure:temurin-{java}-lein` (official) | `dhi.io/eclipse-temurin:{java}` | Official build, DHI runtime | | .NET | `dhi.io/dotnet:{version}-sdk` | `dhi.io/aspnetcore:{version}` | Distroless runtime | **Not supported with DHI:** - **PHP** - FrankenPHP is a specialized server that requires official FrankenPHP images - **Elixir** - Uses official Elixir images (no DHI equivalent available) ## DHI Mirror If you have a private mirror for DHI images (e.g., behind a firewall), use `DHI_MIRROR`: ```bash docker run --rm \ -v /path/to/app:/workspace/source:ro \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=registry.io/my-app:latest \ -e USE_DHI=true \ -e DHI_MIRROR=https://registry.example.io/dhi-io \ miget/migetpacks:latest ``` ## Deployment Considerations Distroless containers have important behavioral differences from traditional containers. ### No Shell Available Commands cannot use shell features like pipes, variable expansion, or shebangs: ```bash Standard (with shell) CMD ["sh", "-c", "node server.js --port ${PORT:-5000}"] ``` ```bash DHI (exec format) CMD ["node", "server.js", "--port", "5000"] ``` Shell variables like `${PORT:-5000}` must be pre-expanded to literal values. migetpacks handles this automatically during Dockerfile generation. ### Ruby-Specific Considerations Ruby scripts with shebangs (`#!/usr/bin/env ruby`) do not work in distroless containers: - Process commands are transformed: `./bin/rails` becomes `ruby bin/rails` - `BUNDLER_VERSION` is set from `Gemfile.lock` to prevent bundler auto-switching (which requires `/usr/bin/env`) - Native gem shared libraries (`.so` files) are copied from the builder stage - PATH includes `/app/bin:/app/vendor/bundle/bin` ### Java-Specific Considerations - Eclipse Temurin JDK images do not include Maven or Gradle - Build tools are installed via `apt-get` in the builder stage - Runtime uses distroless JRE (no JDK) - Version normalization applies: `1.8` becomes `8`, `1.11` becomes `11` ### Go Version Requirements Go DHI images require version 1.22 or newer, with minimum patch versions: - Go 1.22.12+ - Go 1.23.7+ - Go 1.24.1+ ### Result JSON When using DHI, the build result JSON includes `_shell: false` to signal that the container is distroless: ```json { "status": "success", "images": [ {"name": "registry.io/my-app", "ports": ["5000/tcp"]} ], "_shell": false } ``` This can be used by deployment systems to adjust health check and exec strategies. ## Multi-Buildpack with DHI When using `BUILDPACKS` with `USE_DHI=true`, only the primary language runtime is available in the final distroless container. Secondary buildpacks work during the build stage but their runtimes are not included in the runtime image. This is acceptable when secondary buildpacks produce build artifacts (e.g., Node.js compiling frontend assets) rather than running at runtime. --- url: https://migetpacks.com/guides/local-docker --- --- title: "Local Docker Builds" description: "Build and test container images locally using Docker Desktop without pushing to a remote registry." --- ## Overview migetpacks can build container images locally using your Docker socket. When `OUTPUT_IMAGE` does not contain a registry prefix, the built image stays on your local Docker daemon and is never pushed to a remote registry. ## Prerequisites - Docker Desktop (Mac/Windows) or Docker Engine (Linux) - Docker daemon running - The migetpacks image pulled locally ```bash docker pull miget/migetpacks:latest ``` migetpacks uses Docker-in-Docker internally. Docker Desktop on Mac and Windows handles this transparently through its Linux VM. No `--privileged` flag or special configuration is needed. ## Building Locally Mount your application source code and the Docker socket, then run migetpacks: ```bash docker run --rm \ -v $(pwd):/workspace/source:ro \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=my-app:local \ miget/migetpacks:latest ``` ```powershell docker run --rm ` -v ${PWD}:/workspace/source:ro ` -v /var/run/docker.sock:/var/run/docker.sock ` -e OUTPUT_IMAGE=my-app:local ` miget/migetpacks:latest ``` ```bash docker run --rm \ -v $(pwd):/workspace/source:ro \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=my-app:local \ miget/migetpacks:latest ``` Then test the built image: ```bash docker run --rm -p 5000:5000 my-app:local ``` When `OUTPUT_IMAGE` does not contain a `/` with a registry domain (e.g., `my-app:local` vs `registry.io/my-app:latest`), the builder uses the local Docker daemon and the image will not be pushed. ## Language Override By default, migetpacks auto-detects the language. You can override this with the `LANGUAGE` environment variable: ```bash Node.js docker run --rm \ -v /path/to/your/app:/workspace/source:ro \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=my-app:local \ -e LANGUAGE=nodejs \ miget/migetpacks:latest ``` ```bash Python docker run --rm \ -v /path/to/your/app:/workspace/source:ro \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=my-app:local \ -e LANGUAGE=python \ miget/migetpacks:latest ``` ```bash Ruby docker run --rm \ -v /path/to/your/app:/workspace/source:ro \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=my-app:local \ -e LANGUAGE=ruby \ miget/migetpacks:latest ``` ## Custom Environment Variables Any environment variable not recognized by migetpacks is automatically injected into the generated Dockerfile as an `ENV` statement: ```bash docker run --rm \ -v /path/to/your/app:/workspace/source:ro \ -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="http://localhost:3000" \ miget/migetpacks:latest ``` ## Debugging Tips ### Inspect Built Image Contents ```bash docker run --rm my-app:local ls -la /app/ ``` ### Check Environment Variables ```bash docker run --rm my-app:local env ``` ### View the Generated Dockerfile The build output includes the generated Dockerfile content. Look for the `Dockerfile.runtime` section in the build logs. ### Interactive Shell If the build succeeds but the app does not start correctly, you can override the entrypoint: ```bash docker run --rm -it --entrypoint /bin/sh my-app:local ``` DHI (Docker Hardened Images) containers are distroless and do not have a shell. You cannot use `--entrypoint /bin/sh` with DHI-built images. Use standard (non-DHI) builds for debugging. ### Check Exposed Ports ```bash docker inspect my-app:local --format='{{json .Config.ExposedPorts}}' ``` ## Architecture Override By default, migetpacks builds for `x86_64`. To build for a different architecture: ```bash docker run --rm \ -v /path/to/your/app:/workspace/source:ro \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=my-app:local \ -e ARCH=arm64 \ miget/migetpacks:latest ``` ## Source Mount Options Mount the source as read-only to prevent any modifications: ```bash -v /path/to/your/app:/workspace/source:ro ``` If your build process needs to write to the source directory: ```bash -v /path/to/your/app:/workspace/source ``` For monorepo projects, mount the entire repo and use `PROJECT_PATH`: ```bash -v /path/to/monorepo:/workspace/source:ro \ -e PROJECT_PATH=services/api ``` --- url: https://migetpacks.com/guides/monorepo --- --- title: "Monorepo Support" description: "Build specific services or packages within a monorepo using PROJECT_PATH for subdirectory targeting." --- ## Overview migetpacks supports monorepo projects through the `PROJECT_PATH` environment variable. This allows you to target a specific subdirectory within your source tree for language detection, version detection, and building. ## Basic Usage Set `PROJECT_PATH` to the subdirectory you want to build: ```bash docker run --rm \ -v /path/to/monorepo:/workspace/source:ro \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=registry.io/my-api:latest \ -e PROJECT_PATH=services/api \ miget/migetpacks:latest ``` `PROJECT_PATH` is relative to `SOURCE_DIR` (defaults to `/workspace/source`). The builder will detect language and version from the specified subdirectory. ## Example Monorepo Structure ```text my-monorepo/ ├── services/ │ ├── api/ # Go service │ │ ├── go.mod │ │ ├── go.sum │ │ └── main.go │ ├── web/ # Node.js frontend │ │ ├── package.json │ │ └── src/ │ └── worker/ # Python worker │ ├── requirements.txt │ └── worker.py ├── shared/ │ └── proto/ └── docker-compose.yaml ``` ### Building Each Service ```bash API Service (Go) docker run --rm \ -v /path/to/monorepo:/workspace/source:ro \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=registry.io/my-api:latest \ -e PROJECT_PATH=services/api \ miget/migetpacks:latest ``` ```bash Web Frontend (Node.js) docker run --rm \ -v /path/to/monorepo:/workspace/source:ro \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=registry.io/my-web:latest \ -e PROJECT_PATH=services/web \ miget/migetpacks:latest ``` ```bash Worker (Python) docker run --rm \ -v /path/to/monorepo:/workspace/source:ro \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=registry.io/my-worker:latest \ -e PROJECT_PATH=services/worker \ miget/migetpacks:latest ``` ## Combining with DOCKERFILE_PATH For services that have custom Dockerfiles in non-standard locations: ```bash docker run --rm \ -v /path/to/monorepo:/workspace/source:ro \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=registry.io/my-api:latest \ -e PROJECT_PATH=services/api \ -e DOCKERFILE_PATH=docker/Dockerfile.prod \ miget/migetpacks:latest ``` When both `PROJECT_PATH` and `DOCKERFILE_PATH` are set, `DOCKERFILE_PATH` is resolved relative to the `PROJECT_PATH` subdirectory. In the example above, the Dockerfile is located at `services/api/docker/Dockerfile.prod`. ## CI/CD with Monorepos ### GitHub Actions Matrix Build multiple services in parallel using a matrix strategy: ```yaml name: Build Services on: push: branches: [main] jobs: build: runs-on: self-hosted strategy: matrix: service: - name: api path: services/api language: go - name: web path: services/web language: nodejs - name: worker path: services/worker language: python steps: - uses: actions/checkout@v4 - name: Log into registry uses: docker/login-action@v3 with: registry: your-registry.io username: ${{ secrets.REGISTRY_USERNAME }} password: ${{ secrets.REGISTRY_PASSWORD }} - name: Build ${{ matrix.service.name }} run: | docker run --rm \ -v ${{ github.workspace }}:/workspace/source:ro \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=your-registry.io/${{ matrix.service.name }}:${{ github.sha }} \ -e PROJECT_PATH=${{ matrix.service.path }} \ miget/migetpacks:latest ``` ### Shipwright BuildRun ```yaml apiVersion: shipwright.io/v1beta1 kind: BuildRun metadata: name: api-build spec: build: name: my-monorepo paramValues: - name: PROJECT_PATH value: services/api - name: OUTPUT_IMAGE value: registry.io/my-api:latest ``` ## How Language Detection Works with PROJECT_PATH When `PROJECT_PATH` is set, language detection examines only the files within that subdirectory: The effective source becomes `SOURCE_DIR/PROJECT_PATH` (e.g., `/workspace/source/services/api`). Files like `go.mod`, `package.json`, `requirements.txt`, etc. are searched within the subdirectory. Version files (`.nvmrc`, `.ruby-version`, `go.mod`, etc.) are read from the subdirectory. The Docker build context is the subdirectory, so only those files are included in the image. ## Tips - Each service gets its own `OUTPUT_IMAGE`, so they can be deployed independently - Language detection is automatic per subdirectory, no need to set `LANGUAGE` manually - Shared dependencies (e.g., Go workspaces) should be handled via the service's own dependency management - Use `BUILD_CACHE_DIR` with a shared volume to speed up builds across services --- url: https://migetpacks.com/guides/multi-buildpack --- --- title: "Multi-Buildpack" description: "Combine multiple language runtimes in a single build for applications that need more than one language." --- ## Overview migetpacks supports multi-buildpack builds for applications that require multiple language runtimes. For example, a Ruby on Rails app that uses Node.js for asset compilation, or a Python app with a Node.js frontend build step. ## How It Works Multi-buildpack builds use Docker multi-stage builds to copy runtimes from official images into the primary builder stage. All runtimes are available during the build, and builds run sequentially. ```dockerfile # Primary builder with additional runtimes FROM ruby:3.2 AS builder WORKDIR /build # Copy Python runtime from python image COPY --from=python:3.11 /usr/local/bin/python* /usr/local/bin/ COPY --from=python:3.11 /usr/local/lib/python3.11 /usr/local/lib/python3.11 # Copy Node.js runtime from node image COPY --from=node:20 /usr/local/bin/node /usr/local/bin/ COPY --from=node:20 /usr/local/lib/node_modules /usr/local/lib/node_modules # All runtimes available - run builds sequentially RUN bundle install RUN pip install -r requirements.txt RUN npm install && npm run build ``` ## Automatic Detection migetpacks automatically adds Node.js as a secondary buildpack when: - The primary language is **not** Node.js - A `package.json` file exists in the project root This handles the common case of Ruby/Python/Go apps that use Node.js for frontend asset compilation. Automatic Node.js detection only adds Node.js as a secondary buildpack for asset compilation. It does not change the primary language or the runtime base image. ## Explicit Buildpack Ordering Use the `BUILDPACKS` environment variable to explicitly control which buildpacks run and in what order: ```bash docker run --rm \ -v /path/to/app:/workspace/source:ro \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=my-app:latest \ -e BUILDPACKS=ruby,python,nodejs \ miget/migetpacks:latest ``` The first entry determines the base image, runtime container, and main process. In the example above, `ruby` is the primary language. Additional entries (`python`, `nodejs`) copy their runtimes into the builder stage and execute their respective build commands. Each buildpack's install and build commands run in order within the same builder stage. ### Format The `BUILDPACKS` value accepts both short and namespaced formats: ```bash Short Format BUILDPACKS=ruby,python,nodejs ``` ```bash Namespaced Format BUILDPACKS=miget/ruby,miget/python,miget/nodejs ``` When `BUILDPACKS` is set, automatic detection is disabled. Only the specified buildpacks will be used. ## Common Use Cases ### Ruby + Node.js (Asset Compilation) Rails apps with Webpack, esbuild, or Vite for JavaScript bundling: ```bash BUILDPACKS=ruby,nodejs ``` This installs Node.js dependencies (`npm install` or `yarn install`) and runs the build command (`npm run build`) before the Ruby app is finalized. ### Ruby + Python (AI/ML) Ruby apps that use Python libraries for machine learning or data processing: ```bash BUILDPACKS=ruby,python ``` ### Python + Node.js (Frontend Build) Python API with a Node.js-built frontend: ```bash BUILDPACKS=python,nodejs ``` ### Full Stack Example Ruby app with Python for AI features and Node.js for assets: ```bash docker run --rm \ -v /path/to/app:/workspace/source:ro \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=registry.io/my-app:latest \ -e BUILDPACKS=ruby,python,nodejs \ miget/migetpacks:latest ``` ## Runtime Container In the final runtime container (non-DHI builds), all secondary runtimes are copied from the builder stage: ```dockerfile # Runtime stage: copy runtimes from builder FROM ruby:3.2-slim COPY --from=builder /usr/local/bin/python* /usr/local/bin/ COPY --from=builder /usr/local/bin/node /usr/local/bin/ COPY --from=builder /build /app ``` This means all languages are available at runtime, not just during the build. ## DHI Limitation When using `USE_DHI=true` with multiple buildpacks, only the primary language runtime is available in the final container. DHI images are distroless and single-runtime. - **Build-time**: All buildpacks work correctly (uses `-dev` images with shell and package manager) - **Runtime**: Only the primary language is available (distroless images cannot include additional runtimes) A warning is displayed when using `BUILDPACKS` with `USE_DHI=true`. For DHI builds, ensure your application only needs secondary buildpacks at build time (e.g., Node.js for asset compilation that produces static files consumed by the primary runtime). ## app.json Buildpacks You can also specify buildpacks via `app.json`: ```json { "buildpacks": [ {"url": "heroku/nodejs"}, {"url": "heroku/ruby"} ] } ``` migetpacks maps Heroku buildpack names to its internal language identifiers (e.g., `heroku/ruby` becomes `ruby`). --- url: https://migetpacks.com/guides/procfile --- --- title: "Procfile" description: "Define process types with Procfile for web servers, workers, and other processes." --- ## Overview migetpacks supports Procfiles for defining process types in your application. A Procfile declares the commands used to run different process types (web server, background workers, scheduled tasks, etc.). ## Procfile Format A Procfile is a plain text file named `Procfile` (no extension) in the project root. Each line declares a process type: ```text : ``` ### Example Procfile ```text web: bundle exec puma -C config/puma.rb worker: bundle exec sidekiq release: bundle exec rails db:migrate clock: bundle exec clockwork clock.rb ``` ## Process Priority migetpacks determines the main process command using the following priority order: If set, this always takes precedence over any Procfile entry. ```bash -e RUN_COMMAND="node server.js" ``` The `web` process type is the standard entry point for HTTP-serving applications. If no `web:` entry exists, the first process declared in the Procfile is used. If no Procfile exists and `RUN_COMMAND` is not set, a language-specific default is used (e.g., `node server.js`, `python app.py`). ## Language Examples ```text web: bundle exec puma -C config/puma.rb worker: bundle exec sidekiq release: bundle exec rails db:migrate ``` ```text web: node server.js worker: node worker.js ``` ```text web: gunicorn app:app --bind 0.0.0.0:5000 worker: celery -A tasks worker --loglevel=info ``` ```text web: ./bin/app serve worker: ./bin/app worker ``` ```text web: java -jar target/app.jar worker: java -jar target/worker.jar ``` ## Result JSON Process information from the Procfile is recorded in the build result JSON (when `RESULT_FILE` is set): ```json { "status": "success", "processes": { "web": "bundle exec puma -C config/puma.rb", "release": "bundle exec rails db:migrate", "workers": [ {"name": "sidekiq", "command": "bundle exec sidekiq"}, {"name": "clock", "command": "bundle exec clockwork clock.rb"} ] } } ``` ### Process Classification | Process Type | Classification | Description | |-------------|---------------|-------------| | `web` | Main process | HTTP-serving entry point | | `release` | Release script | Run during deployment (e.g., migrations) | | Any other | Worker | Background processes | ## Overriding with RUN_COMMAND You can override the Procfile's web process without modifying the file: ```bash docker run --rm \ -v /path/to/app:/workspace/source:ro \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=registry.io/my-app:latest \ -e RUN_COMMAND="bundle exec puma -p 5000 -w 4" \ miget/migetpacks:latest ``` `RUN_COMMAND` only overrides the main container command (equivalent to Docker `CMD`). Worker processes from the Procfile are still recorded in the result JSON for deployment systems that support multiple process types. ## DHI Considerations When building with `USE_DHI=true`, Procfile commands are adjusted for distroless containers: - Shell-style commands are converted to exec format - Ruby shebang scripts (e.g., `./bin/rails`) are prefixed with `ruby` (e.g., `ruby bin/rails`) - Shell variable expansions like `${PORT:-5000}` are pre-expanded to literal values ## Port Configuration The `PORT` environment variable (default: `5000`) is exposed in the container. Your Procfile command should bind to this port: ```bash Using PORT variable web: bundle exec puma -p $PORT ``` ```bash Hardcoded port web: bundle exec puma -p 5000 ``` ```bash Custom port # Set PORT=8080 when running migetpacks web: node server.js --port $PORT ``` ## app.json Integration If your project includes an `app.json` file, process formation (replicas) can be configured alongside the Procfile: ```json { "formation": { "web": {"quantity": 2, "size": "standard-1x"}, "worker": {"quantity": 1} } } ``` This formation data is included in the result JSON for deployment systems: ```json { "formation": [ {"name": "web", "quantity": 2, "size": "standard-1x"}, {"name": "worker", "quantity": 1, "size": null} ] } ``` --- url: https://migetpacks.com/how-it-works --- --- title: "How It Works" description: "Detection → Version → Dockerfile → Build" --- migetpacks builds your application in four phases. Each phase is handled by a dedicated script inside the builder container. ```mermaid graph LR A[Source Code] --> B[Detect Language] B --> C[Detect Version] C --> D[Generate Dockerfile] D --> E[Build & Push] ``` ## 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 Present | Detected Language | |-------------|-------------------| | `Dockerfile` | dockerfile | | `compose.yaml` / `docker-compose.yml` | compose | | `Gemfile` | ruby | | `package.json` | nodejs | | `deno.json` / `deno.jsonc` | deno | | `bun.lockb` / `bunfig.toml` | bun | | `requirements.txt` / `pyproject.toml` / `setup.py` | python | | `go.mod` | go | | `Cargo.toml` | rust | | `pom.xml` / `build.gradle` | java | | `build.gradle.kts` | kotlin | | `build.sbt` | scala | | `project.clj` | clojure | | `*.csproj` / `*.sln` / `*.slnx` | dotnet | | `mix.exs` | elixir | | `composer.json` | php | Skip auto-detection by setting the `LANGUAGE` environment variable: ```bash -e LANGUAGE=ruby ``` This is useful for polyglot repos where multiple marker files exist. 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. | Language | Version Sources (priority order) | |----------|----------------------------------| | Node.js | `.node-version` → `.nvmrc` → `package.json` engines.node | | Ruby | `.ruby-version` → `Gemfile` ruby declaration | | Python | `.python-version` → `runtime.txt` → `uv.lock` → `pyproject.toml` | | Go | `.go-version` → `go.mod` go directive | | Rust | `rust-toolchain` → `rust-toolchain.toml` channel | | Java | `.java-version` → `system.properties` → `pom.xml` | | .NET | `global.json` → `*.csproj` TargetFramework | | Elixir | `.elixir-version` → `.tool-versions` (asdf) | | Deno | `.deno-version` → `.dvmrc` → `deno.json` version | | Bun | `.bun-version` → `bunfig.toml` version | | PHP | `.php-version` → `composer.json` require.php | Versions are normalized for Docker tag compatibility: ```text 20.x → 20 >=18.0.0 → 18.0 ^3.2.0 → 3.2 20.x || 22.x → 22 3.12.8 → 3.12.8 (kept as-is) ``` The goal is to match available Docker Hub image tags while respecting the developer's version constraint. ## 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: ```dockerfile # 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: ```text 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: ```bash # 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. A Docker-in-Docker daemon starts inside the builder container with BuildKit enabled. Registry credentials and mirrors are configured in the daemon. If `REGISTRY_MIRROR` is set, it is added as a pull-through cache. The generated Dockerfile is built with `docker buildx build`. Cache is read from and written to `CACHE_IMAGE` if configured. If `OUTPUT_IMAGE` contains a registry prefix, the image is pushed. Otherwise, it is loaded into the local Docker daemon. If `RESULT_FILE` is set, build metadata (status, ports, processes, timing) is written as JSON for downstream CI/CD steps. ### Build Cache Options ```bash Registry Cache 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 ``` ```bash Shared Package Cache docker run --rm \ -v ./app:/workspace/source \ -v /var/run/docker.sock:/var/run/docker.sock \ -v /tmp/build-cache:/cache \ -e OUTPUT_IMAGE=my-app:local \ -e BUILD_CACHE_DIR=/cache \ miget/migetpacks:latest ``` ```bash No Cache (Fresh Build) docker run --rm \ -v ./app:/workspace/source \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=my-app:local \ -e NO_CACHE=true \ miget/migetpacks:latest ``` ## Runtime Container The built container follows these conventions: | Property | Value | |----------|-------| | User | `miget` (uid 1000) | | Working directory | `/app` | | Default port | `5000` | | Process manager | None (single process) | | Init system | None (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 Full reference for all builder configuration options. Optimize build times with registry and shared caches. Combine multiple languages in a single build. Use Docker Hardened Images for production. --- url: https://migetpacks.com/introduction --- --- title: "migetpacks" description: "Zero-config container builds for any language" ---

Zero-config, secure, production-ready containers

Drop in your source code and get an optimized, non-root container image. No Dockerfile to write or maintain.

```bash docker pull miget/migetpacks:latest ``` ## Features Auto-detects **14 languages** and their versions from your source code. No Dockerfile, no buildpacks config, no CI scripts to maintain. Optional **Docker Hardened Images** (distroless), non-root runtime user, minimal attack surface, and no shell in production containers. Optimized **layer caching**, registry mirrors, shared package manager caches, and BuildKit for parallel stage execution. Native **Shipwright** integration, GitHub Actions support, result JSON for CI/CD pipelines, and multi-architecture builds. ## Why migetpacks Unlike other build systems, migetpacks uses the same official Docker Hub images you already know (`node:20`, `python:3.12`, `ruby:3.2`), not custom stacks or proprietary base layers. This means: - **Familiar images.** Your production container runs on the same upstream image you'd pick yourself. - **Transparent builds.** migetpacks generates a real multi-stage Dockerfile you can read, debug, and take with you. - **One flag for distroless.** Set `USE_DHI=true` to switch to Docker Hardened Images (dhi.io) with zero config changes. ## Quick Start ```bash docker pull miget/migetpacks:latest ``` ```bash # From your project directory docker run --rm \ -v $PWD:/workspace/source \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=my-app:local \ miget/migetpacks:latest # Or with an explicit path docker run --rm \ -v /path/to/your/app:/workspace/source \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=my-app:local \ miget/migetpacks:latest ``` ```bash docker run --rm -p 5000:5000 my-app:local ``` ## Supported Languages npm, yarn, pnpm deno.json, deps.ts bun.lockb, bunfig.toml pip, uv, poetry Bundler, Rails go.mod, go.sum Cargo.toml Composer, FrankenPHP Maven, Gradle Gradle, Maven sbt, Play Framework Leiningen C#, F#, ASP.NET Mix, Phoenix --- Already have a Dockerfile? migetpacks supports [custom Dockerfiles](/guides/custom-dockerfile) and [Docker Compose](/guides/docker-compose) projects too. ---
Built by miget.com — Unlimited apps, one price.
--- url: https://migetpacks.com/languages/bun --- --- title: "Bun" description: "Build and deploy Bun applications with optimized dependency caching." --- ## Detection migetpacks detects Bun when any of these signals are present: | Priority | Signal | |----------|--------| | 1 | `bun.lock` or `bun.lockb` (lockfiles) | | 2 | `bunfig.toml` (config file) | | 3 | `.bun-version` (version file) | | 4 | `bun-types` in `package.json` dependencies | Bun detection runs after backend frameworks (Ruby, Python, Go, etc.) but before Node.js. If your project has both a `bun.lock` and a `Gemfile`, it will be detected as Ruby with Bun available as a secondary buildpack. ## Version Detection Bun version is resolved in this order: | Priority | Source | Example | |----------|--------|---------| | 1 | `BUN_VERSION` env var | `BUN_VERSION=1.3.4` | | 2 | `.bun-version` file | `1.3.4` | | 3 | `version` in `bunfig.toml` | `version = "1.3.4"` | | 4 | Default | `1.3.4` | ## Build Process migetpacks generates a multi-stage Dockerfile optimized for Bun: ```dockerfile # Builder stage FROM oven/bun:1.3.4 AS builder WORKDIR /build # Layer 1: Copy dependency files (cached if lockfile unchanged) COPY package.json bun.lock ./ # Layer 2: Install dependencies RUN bun install # Layer 3: Copy source code COPY . . # Layer 4: Run build script (if present) RUN bun run build # Runtime stage FROM oven/bun:1.3.4-slim WORKDIR /app COPY --from=builder /build /app ``` ### Entry Point Detection The entry point is automatically detected in this order: 1. `.ts`/`.js` file referenced in `scripts.start` of `package.json` 2. Common file patterns: `index.ts`, `server.ts`, `app.ts`, `main.ts`, `src/index.ts`, `src/server.ts`, `index.js`, `server.js` 3. Falls back to `index.ts` ### Build Script If `package.json` contains a `"build"` script, `bun run build` is automatically executed after dependency installation. You can override this with the `BUILD_COMMAND` environment variable. ### Lockfile Format Bun supports two lockfile formats: - `bun.lock` (text-based, newer format) - `bun.lockb` (binary, legacy format) Both are automatically detected and copied during the build. ## Run Command The default run command is determined in this order: | Priority | Source | Command | |----------|--------|---------| | 1 | `RUN_COMMAND` env var | User-specified | | 2 | `web:` in Procfile | From Procfile | | 3 | Default | `bun run {entrypoint}` | The entry point is detected automatically (see Entry Point Detection above). ## Caching ### Docker Layer Caching Dependencies are installed in a separate layer before source code is copied. `bun install` is only re-run when `package.json` or `bun.lock`/`bun.lockb` changes. ### BuildKit Cache Mounts When `BUILD_CACHE_DIR` is configured, BuildKit cache mounts are used: | Cache Path | Contents | |-----------|----------| | `/cache/bun` | Bun install cache (`~/.bun/install/cache`) | ### Registry Cache Use `CACHE_IMAGE` to push/pull BuildKit inline cache layers to a registry for cross-build caching. ## DHI Support Bun is supported with Docker Hardened Images (requires version 1.3.6 or later). | Stage | Image | |-------|-------| | Build | `dhi.io/bun:{version}-dev` | | Runtime | `dhi.io/bun:{version}` | DHI images for Bun are only available starting from version 1.3.6. If your project uses an older version, migetpacks will fall back to the standard `oven/bun` images. The `-dev` variant includes a shell for building. The runtime image is distroless with no shell, providing a minimal attack surface. ## Example ```bash docker run --rm \ -v /path/to/app:/workspace/source \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=my-bun-app:latest \ miget/migetpacks:latest ``` ### With Custom Options ```bash docker run --rm \ -v /path/to/app:/workspace/source \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=registry.io/app:v1 \ -e BUN_VERSION=1.3.4 \ -e BUILD_COMMAND="bun run build:prod" \ -e USE_DHI=true \ miget/migetpacks:latest ``` --- url: https://migetpacks.com/languages/clojure --- --- title: "Clojure" description: "Build and deploy Clojure applications with migetpacks using Leiningen." --- ## Detection migetpacks detects a Clojure application when either of the following files is present in the project root: - `project.clj` (Leiningen) - `deps.edn` (Clojure CLI/tools.deps) ## Version Detection ### Clojure Version Clojure version is resolved in the following priority order: | Source | Example | |--------|---------| | `.clojure-version` | `1.11.1` | | `project.clj` (org.clojure/clojure dependency) | `[org.clojure/clojure "1.11.1"]` | | `deps.edn` (org.clojure/clojure) | `org.clojure/clojure {:mvn/version "1.11.1"}` | | Default | `1.11.1` | ### Leiningen Version Leiningen version is detected from: | Source | Example | |--------|---------| | `project.clj` (`:min-lein-version`) | `:min-lein-version "2.11.2"` | | Default | `2.11.2` | ### Java Version The JDK version used for compilation and runtime is read from: | Source | Example | |--------|---------| | `system.properties` | `java.runtime.version=21` | | Default | `21` | ## Build Process migetpacks uses the official `clojure:temurin-{java}-lein` Docker image which includes Leiningen and the JDK: ```dockerfile # Build stage FROM clojure:temurin-21-lein AS builder WORKDIR /build # Clojure build environment ENV JAVA_TOOL_OPTIONS="-Dfile.encoding=UTF-8" ENV LEIN_ROOT=1 # Layer caching: copy project.clj first COPY project.clj ./ # Download dependencies (cached if project.clj unchanged) RUN lein deps # Copy source code COPY . . # Build uberjar and cleanup RUN lein uberjar \ && rm -rf src/ test/ dev/ resources/public/js/compiled # Runtime stage FROM eclipse-temurin:21-jre WORKDIR /app COPY --from=builder --chown=1000:1000 /build /app ``` ### Uberjar Detection migetpacks detects custom uberjar names from `project.clj`: ```clojure (defproject myapp "1.0.0" :uberjar-name "myapp-standalone.jar" ...) ``` If `:uberjar-name` is specified, migetpacks uses it when configuring the run command. ## Run Command The default run command for Clojure applications: ```bash java -jar target/uberjar/*-standalone.jar ``` Override with `RUN_COMMAND` or a `Procfile`: ```text web: java -jar target/uberjar/myapp-standalone.jar ``` ### JVM Configuration The following environment variables are set for production: | Variable | Value | |----------|-------| | `JAVA_OPTS` | `-Dfile.encoding=UTF-8 -XX:MaxRAMPercentage=80.0 -Dclojure.main.report=stderr` | | `JAVA_TOOL_OPTIONS` | `-Dfile.encoding=UTF-8` | ## Caching ### Layer Caching Dependencies are cached by copying `project.clj` before the full source: | Cached Files | Purpose | |-------------|---------| | `project.clj` | Leiningen project definition with dependencies | | Scenario | Dependency Install | |----------|-------------------| | First build | Runs | | Source code change only | **Cached** | | project.clj change | Runs | ### BUILD_CACHE_DIR When `BUILD_CACHE_DIR` is set, Leiningen and Maven caches persist at: | Cache Path | Contents | |------------|----------| | lein cache | Leiningen profiles and plugins | | m2 cache | Maven repository (`.m2`) | ## DHI Support Docker Hardened Images are supported for Clojure runtime. Clojure uses a hybrid approach: the official Clojure build image (which includes Leiningen) for building, and a DHI distroless image for runtime. | Stage | Image | |-------|-------| | Build | `clojure:temurin-{java}-lein` (official) | | Runtime | `dhi.io/eclipse-temurin:{java}` | ### DHI Notes - The build image is always the official Clojure image (DHI does not provide Clojure/Leiningen images). - Only the runtime stage uses DHI distroless Eclipse Temurin JRE. - Java version is read from `system.properties` (default: 21). - Runtime images are distroless (no shell). ### Example with DHI ```bash docker run --rm \ -v /path/to/clojure-app:/workspace/source \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=my-clojure-app:latest \ -e USE_DHI=true \ miget/migetpacks:latest ``` ## Example ```bash docker run --rm \ -v /path/to/clojure-app:/workspace/source \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=my-clojure-app:latest \ miget/migetpacks:latest ``` ### With custom JVM options ```bash docker run --rm \ -v /path/to/clojure-app:/workspace/source \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=my-clojure-app:latest \ -e JAVA_OPTS="-Xmx1g -Dclojure.main.report=stderr" \ miget/migetpacks:latest ``` --- url: https://migetpacks.com/languages/deno --- --- title: "Deno" description: "Build and deploy Deno applications with automatic entry point detection." --- ## Detection migetpacks detects Deno when any of these files are present in your project root: - `deno.json` - `deno.jsonc` - `deno.lock` Deno detection takes priority over Node.js. If both `deno.json` and `package.json` exist, migetpacks will detect Deno as the primary language. ## Version Detection Deno version is resolved in this order: | Priority | Source | Example | |----------|--------|---------| | 1 | `DENO_VERSION` env var | `DENO_VERSION=2.0` | | 2 | `.deno-version` file | `2.6.1` | | 3 | `.dvmrc` file | `2.6.1` | | 4 | `version` field in `deno.json` | `"2.6.1"` | | 5 | Default | `2.6.1` | ## Build Process migetpacks generates a Dockerfile that caches dependencies separately from source code: ```dockerfile FROM denoland/deno:2.6.1 AS builder WORKDIR /build # Set Deno cache inside build dir ENV DENO_DIR=/build/.deno-cache # Layer 1: Copy dependency files (cached if deno.json unchanged) COPY deno.json deno.lock* ./ # Layer 2: Install dependencies RUN deno install # Layer 3: Copy source code COPY . . # Layer 4: Cache and compile entry point RUN deno cache main.ts # Runtime stage FROM denoland/deno:2.6.1 WORKDIR /app COPY --from=builder /build /app ENV DENO_DIR=/app/.deno-cache ``` ### Entry Point Detection The entry point is automatically detected in this order: 1. `start` task in `deno.json` (extracts the `.ts`/`.js` file from the command) 2. Common file patterns: `main.ts`, `mod.ts`, `server.ts`, `app.ts`, `index.ts`, `index.js`, `main.js`, `src/main.ts`, `src/mod.ts` 3. Falls back to `main.ts` ### Dependency Caching If `deno.json` contains an `imports` field, `deno install` is run in a separate layer before source code is copied. This ensures dependencies are cached when only source code changes. ## Run Command The default run command is determined in this order: | Priority | Source | Command | |----------|--------|---------| | 1 | `RUN_COMMAND` env var | User-specified | | 2 | `web:` in Procfile | From Procfile | | 3 | Default | `deno run --allow-net --allow-read --allow-env {entrypoint}` | The entry point is detected automatically (see Entry Point Detection above). ## Caching ### Docker Layer Caching Dependencies are installed from `deno.json` in a separate layer. The `deno cache` command pre-compiles the entry point and its dependencies, which are stored in `DENO_DIR` inside the build directory. ### Registry Cache Use `CACHE_IMAGE` to push/pull BuildKit inline cache layers to a registry for cross-build caching. Deno does not use BuildKit cache mounts since dependency caching is handled through the `DENO_DIR` environment variable and Docker layer caching. ## DHI Support Deno is supported with Docker Hardened Images (requires version 2.6.4 or later). | Stage | Image | |-------|-------| | Build | `dhi.io/deno:{version}-dev` | | Runtime | `dhi.io/deno:{version}` | DHI images for Deno are only available starting from version 2.6.4. If your project uses an older version, migetpacks will fall back to the standard `denoland/deno` images. The DHI runtime image runs as the `deno` user (non-root) and is distroless with no shell. ## Example ```bash docker run --rm \ -v /path/to/app:/workspace/source \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=my-deno-app:latest \ miget/migetpacks:latest ``` ### With Custom Options ```bash docker run --rm \ -v /path/to/app:/workspace/source \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=registry.io/app:v1 \ -e DENO_VERSION=2.6.1 \ -e RUN_COMMAND="deno run --allow-all server.ts" \ -e USE_DHI=true \ miget/migetpacks:latest ``` --- url: https://migetpacks.com/languages/dotnet --- --- title: ".NET" description: "Build and deploy .NET/C# applications with migetpacks." --- ## Detection migetpacks detects a .NET application when any of the following are present: - `*.sln` or `*.slnx` (solution files, root level) - `global.json` (root level) - `*.csproj`, `*.fsproj`, or `*.vbproj` (project files, up to 2 levels deep) ## Version Detection .NET SDK version is resolved in the following priority order: | Source | Example | |--------|---------| | `global.json` (`sdk.version`) | `{"sdk": {"version": "8.0.100"}}` | | `*.csproj` (`TargetFramework`) | `net8.0` | | Default | `8.0` | The `TargetFramework` value has the `net` prefix stripped (e.g., `net8.0` becomes `8.0`). ## Build Process migetpacks generates a multi-stage Dockerfile using the official .NET SDK and ASP.NET runtime images: ```dockerfile # Build stage FROM mcr.microsoft.com/dotnet/sdk:8.0 AS builder WORKDIR /build # .NET build optimizations ENV NUGET_XMLDOC_MODE=skip ENV DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 ENV DOTNET_CLI_TELEMETRY_OPTOUT=1 ENV DOTNET_NOLOGO=1 # Copy source code COPY . . # Restore, build, and publish RUN dotnet restore \ && dotnet publish --configuration Release --no-restore -p:PublishDir=bin/publish # Runtime stage FROM mcr.microsoft.com/dotnet/aspnet:8.0 WORKDIR /app COPY --from=builder --chown=1000:1000 /build /app ``` ### Source Code Cleanup Since .NET is a compiled language, source code is completely removed from the runtime image after publishing: | Removed | Reason | |---------|--------| | `*.cs`, `*.fs`, `*.vb` | Source files (compiled to DLLs) | | `*.csproj`, `*.fsproj`, `*.vbproj` | Project files | | `*.sln`, `*.slnx` | Solution files | | `obj/` | Intermediate build files | | `bin/Release/net*/`, `bin/Debug/` | Non-publish build output | Only the `bin/publish/` directory contents are kept in the final image. ### .NET Tool Restore If a `.config/dotnet-tools.json` is present, `dotnet tool restore` is run before the build to install any required .NET CLI tools. ## Run Command The default run command for .NET applications: ```bash dotnet MyApp.dll ``` migetpacks detects the main DLL from the publish output. Override with `RUN_COMMAND` or a `Procfile`: ```text web: dotnet MyApp.dll --urls http://0.0.0.0:5000 ``` ### Runtime Configuration The following environment variables are set for production: | Variable | Value | |----------|-------| | `ASPNETCORE_ENVIRONMENT` | `Production` | | `DOTNET_RUNNING_IN_CONTAINER` | `true` | | `DOTNET_SYSTEM_GLOBALIZATION_INVARIANT` | `1` | | `DOTNET_CLI_TELEMETRY_OPTOUT` | `1` | | `ASPNETCORE_HTTP_PORTS` | `5000` | ## Caching ### Layer Caching The .NET build uses NuGet cache mounts for dependency caching: | Scenario | Dependency Restore | |----------|-------------------| | First build | Runs | | Source code change only | NuGet packages cached via mount | | Project file change | Runs restore | ### BUILD_CACHE_DIR When `BUILD_CACHE_DIR` is set, NuGet packages are cached via BuildKit mount at: | Cache Path | Contents | |------------|----------| | NuGet packages mount | `~/.nuget/packages` | ## DHI Support Docker Hardened Images are supported for .NET builds. | Stage | Image | |-------|-------| | Build | `dhi.io/dotnet:{version}-sdk` | | Runtime | `dhi.io/aspnetcore:{version}` | ### DHI Notes - Build SDK images include the full .NET SDK for compilation. - Runtime images are based on ASP.NET Core distroless images (no shell, no `apt-get`). - The `nonroot` user is used instead of the `miget` user. - Suitable for web APIs and ASP.NET Core applications. ### Example with DHI ```bash docker run --rm \ -v /path/to/dotnet-app:/workspace/source \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=my-dotnet-app:latest \ -e USE_DHI=true \ miget/migetpacks:latest ``` ## Example ```bash docker run --rm \ -v /path/to/dotnet-app:/workspace/source \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=my-dotnet-app:latest \ miget/migetpacks:latest ``` ### Monorepo with PROJECT_PATH ```bash docker run --rm \ -v /path/to/monorepo:/workspace/source \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=my-api:latest \ -e PROJECT_PATH=src/MyApi \ miget/migetpacks:latest ``` ### With custom build command ```bash docker run --rm \ -v /path/to/dotnet-app:/workspace/source \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=my-dotnet-app:latest \ -e BUILD_COMMAND="dotnet publish -c Release -o /build/out" \ miget/migetpacks:latest ``` --- url: https://migetpacks.com/languages/elixir --- --- title: "Elixir" description: "Build and deploy Elixir and Phoenix applications with migetpacks." --- ## Detection migetpacks detects an Elixir application when either of the following files is present in the project root: - `mix.exs` - `mix.lock` ## Version Detection ### Elixir Version Elixir version is resolved in the following priority order: | Source | Example | |--------|---------| | `.elixir-version` | `1.18.4` | | `.tool-versions` (asdf) | `elixir 1.18.4` | | Default | `1.18.4` | ### Erlang/OTP Version Erlang version is resolved from: | Source | Example | |--------|---------| | `.erlang-version` | `27.0` | | `.tool-versions` (asdf) | `erlang 27.0` | | Default | Auto-compatible with Elixir version | If no Erlang version is specified, the Elixir Docker image's bundled Erlang/OTP version is used, which is guaranteed to be compatible. ## Build Process migetpacks generates a multi-stage Dockerfile using official Elixir images: ```dockerfile # Build stage FROM elixir:1.18.4 AS builder WORKDIR /build # Elixir build environment ENV MIX_ENV=prod ENV ERL_FLAGS="-noinput" # Layer caching: copy dependency files first COPY mix.exs mix.lock* ./ COPY config ./config/ # Install Hex, Rebar, and fetch dependencies RUN mix local.hex --force \ && mix local.rebar --force \ && mix deps.get --only prod # Compile dependencies separately RUN mix deps.compile # Copy source code COPY . . # Compile and build release RUN mix compile RUN mix release # Runtime stage FROM elixir:1.18.4-slim WORKDIR /app COPY --from=builder --chown=1000:1000 /build /app ``` ### Phoenix Framework Detection If `mix.exs` contains a `:phoenix` dependency, migetpacks automatically: 1. Copies `config/` early (needed for dependency compilation) 2. Compiles assets using `mix assets.deploy` (or falls back to npm-based asset pipeline) 3. Runs `mix phx.digest` for static file fingerprinting ```dockerfile # Phoenix asset compilation RUN mix compile RUN if [ -d "assets" ]; then \ mix assets.deploy 2>/dev/null || \ (cd assets && npm install && npm run deploy && cd .. && mix phx.digest); \ fi ``` ### Umbrella Project Detection If an `apps/` directory exists, migetpacks detects an umbrella project and copies child `mix.exs` files for proper dependency resolution. ### Mix Releases migetpacks detects if the project uses [Mix releases](https://hexdocs.pm/mix/Mix.Tasks.Release.html) by checking for `releases:` in `mix.exs`. When releases are used: - The release binary is self-contained (no Hex/Mix needed at runtime) - The runtime image is slimmer - The run command uses the release binary When releases are **not** used: - Hex and Mix are copied to the runtime image - Git is installed at runtime (needed for git dependency verification) - `mix phx.server` is used as the run command ## Run Command The default run command depends on whether Mix releases are configured: | Configuration | Default Command | |--------------|----------------| | With releases | `/app/bin/{app_name} start` | | Without releases (Phoenix) | `mix phx.server` | | Without releases (plain) | `mix run --no-halt` | Override with `RUN_COMMAND` or a `Procfile`: ```text web: /app/bin/myapp start worker: /app/bin/myapp eval "MyApp.Worker.start()" ``` ### Runtime Configuration | Variable | Value | |----------|-------| | `MIX_ENV` | `prod` | | `HOME` | `/home/miget` | ## Caching ### Layer Caching Dependencies are cached by copying `mix.exs` and `mix.lock` before the full source: | Cached Files | Purpose | |-------------|---------| | `mix.exs` | Project definition with dependencies | | `mix.lock` | Locked dependency versions | | `config/` | Configuration (Phoenix needs this for deps.compile) | | Scenario | Dependency Install | |----------|-------------------| | First build | Runs | | Source code change only | **Cached** | | mix.exs/mix.lock change | Runs | ### BUILD_CACHE_DIR When `BUILD_CACHE_DIR` is set, Hex and Mix dependency caches persist at: | Cache Path | Contents | |------------|----------| | hex cache | Hex packages | | mix cache | Mix dependencies and compiled artifacts | ## DHI Support Docker Hardened Images (DHI) are **not supported** for Elixir. Official Elixir images are used for both build and runtime stages. When `USE_DHI=true` is set, migetpacks will display a warning and continue using the official Elixir images: | Stage | Image | |-------|-------| | Build | `elixir:{version}` | | Runtime | `elixir:{version}-slim` | ## Example ```bash docker run --rm \ -v /path/to/elixir-app:/workspace/source \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=my-elixir-app:latest \ miget/migetpacks:latest ``` ### Phoenix application ```bash docker run --rm \ -v /path/to/phoenix-app:/workspace/source \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=my-phoenix-app:latest \ miget/migetpacks:latest ``` ### Running Phoenix Phoenix requires `SECRET_KEY_BASE` at runtime. Generate one with `mix phx.gen.secret` or `openssl rand -hex 64`: ```bash docker run -p 5000:5000 \ -e SECRET_KEY_BASE=$(openssl rand -hex 64) \ my-phoenix-app:latest ``` For production with a database: ```bash docker run -p 5000:5000 \ -e SECRET_KEY_BASE=your-production-secret-key \ -e DATABASE_URL=ecto://user:pass@host/db \ my-phoenix-app:latest ``` ### With build cache ```bash docker run --rm \ -v /path/to/elixir-app:/workspace/source \ -v /var/run/docker.sock:/var/run/docker.sock \ -v /tmp/build-cache:/cache \ -e OUTPUT_IMAGE=my-elixir-app:latest \ -e BUILD_CACHE_DIR=/cache \ miget/migetpacks:latest ``` --- url: https://migetpacks.com/languages/go --- --- title: "Go" description: "Build and deploy Go applications as statically-linked binaries with distroless runtime." --- ## Detection migetpacks detects Go when any of these files are present in your project root: - `go.mod` - `Godeps/Godeps.json` - `glide.yaml` ## Version Detection Go version is resolved in this order: | Priority | Source | Example | |----------|--------|---------| | 1 | `GO_VERSION` env var | `GO_VERSION=1.23` | | 2 | `.go-version` file | `1.25.0` | | 3 | `go` directive in `go.mod` | `go 1.23` | | 4 | Default | `1.25.0` | ## Build Process migetpacks generates a multi-stage Dockerfile that produces a static binary: ```dockerfile # Builder stage FROM golang:1.25 AS builder WORKDIR /build # Go build environment (static linking) ENV CGO_ENABLED=0 ENV GOOS=linux # Layer 1: Copy go.mod/go.sum for layer caching COPY go.mod go.sum* ./ # Layer 2: Download dependencies (CACHED when go.mod unchanged) RUN go mod download # Layer 3: Copy source code COPY . . # Layer 4: Build static binary and cleanup RUN go build -ldflags="-s -w" -o /build/myapp . \ && rm -rf .git *.go go.mod go.sum vendor/ # Runtime stage FROM debian:bookworm-slim RUN apt-get update && apt-get install -y ca-certificates WORKDIR /app COPY --from=builder /build /app ``` ### Static Linking Go builds use `CGO_ENABLED=0` by default, producing fully static binaries that run on minimal base images. The linker flags `-s -w` strip debug information for smaller binaries. ### Module Name and Binary Detection The binary name is automatically derived from the module name in `go.mod`: - `module github.com/user/myapp` produces binary `myapp` - Falls back to `app` if detection fails ### Package Spec You can specify which packages to build using the `//+heroku install` comment in `go.mod`: ```go module github.com/user/myapp // +heroku install ./cmd/api ./cmd/worker go 1.23 ``` Without this directive, `go build .` is used (builds the package in the current directory). For multiple packages, binaries are placed in `/build/bin/`: ```dockerfile RUN go build -ldflags="-s -w" -o /build/bin/ ./cmd/... ``` ### Build Hooks migetpacks supports pre- and post-compile hooks: - `bin/go-pre-compile` - Runs before `go build` - `bin/go-post-compile` - Runs after `go build` ### golang-migrate If `github.com/golang-migrate/migrate` is detected in `go.mod`, the `migrate` CLI tool is automatically installed in the builder stage. ### Linker Symbols Set custom linker symbols using environment variables: ```bash -e GO_LINKER_SYMBOL=main.version -e GO_LINKER_VALUE=1.0.0 ``` This adds `-X main.version=1.0.0` to the linker flags. ## Run Command The default run command is determined in this order: | Priority | Source | Command | |----------|--------|---------| | 1 | `RUN_COMMAND` env var | User-specified | | 2 | `web:` in Procfile | From Procfile | | 3 | Default | `./app` (or `./myapp` based on module name) | Binary names in Procfiles are automatically prefixed with `./` if they don't already have a path prefix. ## Caching ### Docker Layer Caching Dependencies are downloaded in a separate layer. `go mod download` is only re-run when `go.mod` or `go.sum` changes. ### BuildKit Cache Mounts When `BUILD_CACHE_DIR` is configured, BuildKit cache mounts are used: | Cache Path | Contents | |-----------|----------| | `/cache/go` | Go module cache (`/go/pkg/mod`) and build cache (`~/.cache/go-build`) | ### Registry Cache Use `CACHE_IMAGE` to push/pull BuildKit inline cache layers to a registry for cross-build caching. ## DHI Support Go is supported with Docker Hardened Images (requires version 1.22 or later). | Stage | Image | |-------|-------| | Build | `dhi.io/golang:{version}-dev` | | Runtime | `dhi.io/golang:{version}` | DHI images for Go require minimum patch versions: 1.22.12, 1.23.7, or 1.24.1+. Older patch versions within these minor releases are not available. The DHI runtime image is distroless with no shell. Since Go produces static binaries, this works without any special handling. ### Go Toolchain in Runtime If you need the Go toolchain available at runtime (for plugins, `go run`, etc.): ```bash -e GO_INSTALL_TOOLS_IN_IMAGE=true ``` This copies `/usr/local/go` from the builder to the runtime image. ## Example ```bash docker run --rm \ -v /path/to/app:/workspace/source \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=my-go-app:latest \ miget/migetpacks:latest ``` ### With Custom Options ```bash docker run --rm \ -v /path/to/app:/workspace/source \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=registry.io/app:v1 \ -e GO_VERSION=1.23 \ -e GO_LINKER_SYMBOL=main.version \ -e GO_LINKER_VALUE=1.2.3 \ -e USE_DHI=true \ miget/migetpacks:latest ``` ### Multi-Binary Project ```bash docker run --rm \ -v /path/to/app:/workspace/source \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=registry.io/app:v1 \ -e RUN_COMMAND="./bin/api" \ miget/migetpacks:latest ``` With `// +heroku install ./cmd/api ./cmd/worker` in `go.mod`, both binaries are available at `/app/bin/api` and `/app/bin/worker`. --- url: https://migetpacks.com/languages/java --- --- title: "Java" description: "Build and deploy Java applications with migetpacks using Maven or Gradle." --- ## Detection migetpacks detects a Java application when any of the following files are present in the project root: - `pom.xml` (Maven) - `build.gradle` (Gradle, Groovy DSL) - `build.gradle.kts` (Gradle, Kotlin DSL) ## Version Detection Java version is resolved in the following priority order: | Source | Example | |--------|---------| | `.java-version` | `21` | | `system.properties` | `java.runtime.version=21` | | `pom.xml` (`` or ``) | `21` | | Default | `21` | ### Version Normalization Legacy version formats are normalized for Docker image compatibility: | Input | Normalized | |-------|-----------| | `1.8` | `8` | | `1.11` | `11` | | `17` | `17` | | `21` | `21` | ## Build Process ### Build Tool Detection migetpacks automatically detects the build tool: | File Present | Build Tool | Build Image | |-------------|-----------|-------------| | `pom.xml` | Maven | `maven:3-eclipse-temurin-{version}` | | `build.gradle` or `build.gradle.kts` | Gradle | `gradle:jdk{version}` | ### Gradle Wrapper If `gradlew` is present in the project root, migetpacks uses the Gradle Wrapper instead of the system Gradle for correct version compatibility. ### Generated Dockerfile ```dockerfile # Build stage FROM maven:3-eclipse-temurin-21 AS builder WORKDIR /build # Layer caching: copy build files first COPY pom.xml ./ # Download dependencies (cached if pom.xml unchanged) RUN mvn dependency:go-offline -B || true # Copy source code COPY . . # Build and cleanup source RUN mvn package -DskipTests -B \ && rm -rf src/ .mvn/ mvnw* pom.xml # Runtime stage FROM eclipse-temurin:21-jre WORKDIR /app COPY --from=builder --chown=1000:1000 /build /app ``` For Gradle projects: ```dockerfile FROM gradle:jdk21 AS builder WORKDIR /build COPY build.gradle* settings.gradle* ./ RUN gradle dependencies --no-daemon || true COPY . . RUN gradle build -x test --no-daemon \ && rm -rf src/ gradle/ gradlew* build.gradle* ``` ## Run Command The default run command depends on the build tool output: ```bash java -jar target/*.jar ``` Override with `RUN_COMMAND` or a `Procfile`: ```text web: java -jar target/myapp-1.0.jar ``` ### JVM Configuration The following environment variables are set for production: | Variable | Value | |----------|-------| | `JAVA_OPTS` | `-Dfile.encoding=UTF-8 -XX:MaxRAMPercentage=80.0` | | `JAVA_TOOL_OPTIONS` | `-Dfile.encoding=UTF-8` | ## Caching ### Layer Caching Dependencies are cached by copying build definition files before the full source: | Build Tool | Cached Files | |-----------|-------------| | Maven | `pom.xml` | | Gradle | `build.gradle*`, `settings.gradle*`, `gradlew`, `gradle/` | | Scenario | Dependency Install | |----------|-------------------| | First build | Runs | | Source code change only | **Cached** | | Build file change | Runs | ### BUILD_CACHE_DIR When `BUILD_CACHE_DIR` is set, build tool caches persist at: | Cache Path | Contents | |------------|----------| | `/cache/maven` | Maven repository (`~/.m2/repository`) | | `/cache/gradle` | Gradle caches and wrapper | ## DHI Support Docker Hardened Images are supported for Java builds. | Stage | Image | |-------|-------| | Build | `dhi.io/eclipse-temurin:{version}-jdk-dev` | | Runtime | `dhi.io/eclipse-temurin:{version}` | ### DHI Notes - Eclipse Temurin DHI images do **not** include Maven or Gradle. Build tools are installed via `apt-get` in the builder stage. - Runtime images are distroless (no shell, no `apt-get`). - The `nonroot` user is used instead of the `miget` user. - Version normalization applies: `1.8` becomes `8`, `1.11` becomes `11`. ### Example with DHI ```bash docker run --rm \ -v /path/to/java-app:/workspace/source \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=my-java-app:latest \ -e USE_DHI=true \ miget/migetpacks:latest ``` ## Example ```bash docker run --rm \ -v /path/to/java-app:/workspace/source \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=my-java-app:latest \ miget/migetpacks:latest ``` ### With build cache ```bash docker run --rm \ -v /path/to/java-app:/workspace/source \ -v /var/run/docker.sock:/var/run/docker.sock \ -v /tmp/build-cache:/cache \ -e OUTPUT_IMAGE=my-java-app:latest \ -e BUILD_CACHE_DIR=/cache \ miget/migetpacks:latest ``` --- url: https://migetpacks.com/languages/kotlin --- --- title: "Kotlin" description: "Build and deploy Kotlin applications with migetpacks using Gradle." --- ## Detection migetpacks detects a Kotlin application when any of the following conditions are met: - `settings.gradle.kts` exists - `.kt` files exist in `src/` directory - `build.gradle.kts` contains the `kotlin` plugin - `build.gradle` (Groovy DSL) contains a Kotlin plugin reference Kotlin detection takes priority over Java/Gradle detection. If your project has `build.gradle.kts` with Kotlin plugins, it will be identified as Kotlin rather than Java. ## Version Detection ### Kotlin Version Kotlin version is resolved in the following priority order: | Source | Example | |--------|---------| | `.kotlin-version` | `2.1.0` | | `gradle.properties` (`kotlin.version`) | `kotlin.version=2.1.0` | | `build.gradle.kts` (plugin version) | `kotlin("jvm") version "2.1.0"` | | Default | `2.1.0` | ### Java Version The JDK version used for compilation and runtime is read from: | Source | Example | |--------|---------| | `system.properties` | `java.runtime.version=21` | | Default | `21` | ## Build Process migetpacks generates a multi-stage Dockerfile using Eclipse Temurin JDK with Gradle: ```dockerfile # Build stage FROM gradle:jdk21 AS builder WORKDIR /build # Kotlin build environment ENV JAVA_TOOL_OPTIONS="-Dfile.encoding=UTF-8" # Layer caching: copy Gradle build files first COPY gradlew ./ COPY gradle/ gradle/ COPY build.gradle* settings.gradle* ./ # Download dependencies (cached if build files unchanged) RUN ./gradlew dependencies --no-daemon || true # Copy source code COPY . . # Build and cleanup RUN ./gradlew build -x test --no-daemon \ && rm -rf src/ gradle/ gradlew* build.gradle* settings.gradle* # Runtime stage FROM eclipse-temurin:21-jre WORKDIR /app COPY --from=builder --chown=1000:1000 /build /app ``` ### Gradle Wrapper If `gradlew` is present in the project root, migetpacks uses the Gradle Wrapper for correct version compatibility. Otherwise, it falls back to the system Gradle. ## Run Command The default run command for Kotlin applications: ```bash java -jar build/libs/*.jar ``` Override with `RUN_COMMAND` or a `Procfile`: ```text web: java -jar build/libs/myapp-1.0-all.jar ``` ### JVM Configuration The following environment variables are set for production: | Variable | Value | |----------|-------| | `JAVA_OPTS` | `-Dfile.encoding=UTF-8 -XX:MaxRAMPercentage=80.0` | | `JAVA_TOOL_OPTIONS` | `-Dfile.encoding=UTF-8` | ## Caching ### Layer Caching Dependencies are cached by copying Gradle build files before the full source: | Cached Files | Purpose | |-------------|---------| | `build.gradle.kts` / `build.gradle` | Build configuration | | `settings.gradle.kts` / `settings.gradle` | Project settings | | `gradlew` + `gradle/` | Gradle Wrapper | | Scenario | Dependency Install | |----------|-------------------| | First build | Runs | | Source code change only | **Cached** | | Build file change | Runs | ### BUILD_CACHE_DIR When `BUILD_CACHE_DIR` is set, Gradle caches persist at: | Cache Path | Contents | |------------|----------| | `/cache/gradle` | Gradle caches and wrapper downloads | ## DHI Support Docker Hardened Images are supported for Kotlin builds. | Stage | Image | |-------|-------| | Build | `dhi.io/eclipse-temurin:21-jdk-dev` | | Runtime | `dhi.io/eclipse-temurin:21` | ### DHI Notes - Java version is read from `system.properties` (`java.runtime.version`), defaulting to 21. - Eclipse Temurin DHI JDK images include the JDK but Gradle must be available (via wrapper or system install). - Runtime images are distroless (no shell). - The `nonroot` user is used instead of the `miget` user. ### Example with DHI ```bash docker run --rm \ -v /path/to/kotlin-app:/workspace/source \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=my-kotlin-app:latest \ -e USE_DHI=true \ miget/migetpacks:latest ``` ## Example ```bash docker run --rm \ -v /path/to/kotlin-app:/workspace/source \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=my-kotlin-app:latest \ miget/migetpacks:latest ``` ### With build cache ```bash docker run --rm \ -v /path/to/kotlin-app:/workspace/source \ -v /var/run/docker.sock:/var/run/docker.sock \ -v /tmp/build-cache:/cache \ -e OUTPUT_IMAGE=my-kotlin-app:latest \ -e BUILD_CACHE_DIR=/cache \ miget/migetpacks:latest ``` --- url: https://migetpacks.com/languages/nodejs --- --- title: "Node.js" description: "Build and deploy Node.js applications with automatic package manager detection." --- ## Detection migetpacks detects Node.js when any of these files are present in your project root: - `package.json` - `package-lock.json` - `yarn.lock` - `pnpm-lock.yaml` ### Package Manager Detection The package manager is detected from lockfiles first, then from the `packageManager` field in `package.json`: | Priority | Signal | Result | |----------|--------|--------| | 1 | `pnpm-lock.yaml` exists | pnpm | | 2 | `yarn.lock` exists | yarn | | 3 | `package-lock.json` exists | npm | | 4 | `packageManager` field in `package.json` | npm, yarn, or pnpm | | 5 | `package.json` exists (no lockfile) | npm | If multiple lockfiles are detected (e.g., both `yarn.lock` and `package-lock.json`), migetpacks will warn you and use the highest-priority lockfile. Remove unused lockfiles to avoid confusion. ## Version Detection Node.js version is resolved in this order: | Priority | Source | Example | |----------|--------|---------| | 1 | `NODE_VERSION` env var | `NODE_VERSION=20` | | 2 | `.node-version` file | `22.16.0` | | 3 | `.nvmrc` file | `20` | | 4 | `engines.node` in `package.json` | `">=20"` | | 5 | Default | `22.16.0` | Version constraints are normalized for Docker compatibility: - `20.x` becomes `20` - `>=18` becomes `18` - `^20.0.0` becomes `20.0` - `20.x || 22.x || 24.x` becomes `24` (highest major) ## Build Process migetpacks generates a multi-stage Dockerfile optimized for layer caching: ```dockerfile # Builder stage FROM node:22 AS builder WORKDIR /build # Layer 1: Copy dependency files (cached if lockfiles unchanged) COPY package.json package-lock.json ./ ENV NODE_ENV=production # Layer 2: Install dependencies (CACHED when only source code changes) RUN npm ci # Layer 3: Copy source code COPY . . # Layer 4: Build and prune RUN npm run build \ && npm prune --production \ && rm -rf .git .github test tests spec __tests__ coverage .nyc_output .cache # Runtime stage FROM node:22-slim WORKDIR /app COPY --from=builder /build /app ENV NODE_ENV=production ``` ### Package Manager Commands | Manager | Install | Prune | |---------|---------|-------| | npm | `npm ci` (or `npm install`) | `npm prune --production` | | yarn | `yarn install --frozen-lockfile` | `yarn install --production --frozen-lockfile --ignore-scripts` | | pnpm | `pnpm install --frozen-lockfile` | `pnpm prune --prod` | ### Build Script If `package.json` contains a `"build"` script, it is automatically executed after dependency installation. You can override this with the `BUILD_COMMAND` environment variable. ### Runtime Cleanup The following directories are removed from the final image: - `.git/`, `.github/` - `test/`, `tests/`, `spec/`, `__tests__/` - `coverage/`, `.nyc_output/`, `.cache/` ## Run Command The default run command is determined in this order: | Priority | Source | Command | |----------|--------|---------| | 1 | `RUN_COMMAND` env var | User-specified | | 2 | `web:` in Procfile | From Procfile | | 3 | `scripts.start` in `package.json` | `npm start` | | 4 | Default | `node index.js` | ## Caching ### Docker Layer Caching Dependencies are installed in a separate layer before source code is copied. This means `npm ci` is only re-run when `package.json` or `package-lock.json` changes. ### BuildKit Cache Mounts When `BUILD_CACHE_DIR` is configured, BuildKit cache mounts are used for package manager caches: | Manager | Cache Path | |---------|-----------| | npm | `/cache/npm` | | yarn | `/cache/yarn` | | pnpm | `/cache/pnpm` | ### Registry Cache Use `CACHE_IMAGE` to push/pull BuildKit inline cache layers to a registry for cross-build caching. ## DHI Support Node.js is fully supported with Docker Hardened Images. | Stage | Image | |-------|-------| | Build | `dhi.io/node:{version}-dev` | | Runtime | `dhi.io/node:{version}` | The `-dev` variant includes a shell and package manager for building. The runtime image is distroless with no shell, providing a minimal attack surface. DHI runtime images run as the `node` user (non-root). Commands must use exec format since there is no `/bin/sh` available. ## Example ```bash docker run --rm \ -v /path/to/app:/workspace/source \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=my-node-app:latest \ miget/migetpacks:latest ``` ### With Custom Options ```bash docker run --rm \ -v /path/to/app:/workspace/source \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=registry.io/app:v1 \ -e NODE_VERSION=20 \ -e BUILD_COMMAND="npm run build:prod" \ -e NODE_OPTIONS="--max-old-space-size=4096" \ -e USE_DHI=true \ miget/migetpacks:latest ``` ### Environment Variables Any environment variable not recognized by migetpacks is automatically injected into the generated Dockerfile. Common Node.js variables: - `NODE_OPTIONS` - Node.js runtime options (e.g., `--max-old-space-size=4096`) - `VITE_*` - Vite build-time variables - `NEXT_PUBLIC_*` - Next.js public environment variables --- url: https://migetpacks.com/languages/php --- --- title: "PHP" description: "Build and deploy PHP applications with migetpacks using FrankenPHP." --- ## Detection migetpacks detects a PHP application when any of the following files are present in the project root: - `composer.json` - `index.php` ## Version Detection PHP version is resolved in the following priority order: | Source | Example | |--------|---------| | `.php-version` | `8.3` | | `composer.json` (`require.php`) | `"php": ">=8.2"` | | Default | `8.3` | Version constraints from `composer.json` (e.g., `>=8.2`, `^8.3`) are normalized to Docker-compatible versions. FrankenPHP supports PHP 8.2, 8.3, and 8.4. Older versions will fall back to PHP 8.2 with a warning. ## Build Process migetpacks generates a multi-stage Dockerfile using [FrankenPHP](https://frankenphp.dev/) (a modern PHP application server built on Go and Caddy): ```dockerfile # Build stage FROM dunglas/frankenphp:builder-php8.3 AS builder WORKDIR /build # Install PHP extensions detected from composer.json RUN apt-get update && apt-get install -y ... \ && docker-php-ext-install pdo_pgsql gd ... # Layer caching: copy dependency files first COPY composer.json composer.lock* ./ # Install Composer and dependencies (cached if lockfiles unchanged) RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer \ && composer install --no-dev --optimize-autoloader --no-scripts # Copy source code COPY . . # Runtime stage FROM dunglas/frankenphp:php8.3 WORKDIR /app COPY --from=builder --chown=1000:1000 /build /app ``` ### PHP Extension Detection Extensions declared in `composer.json` under `require` with the `ext-` prefix are automatically installed: ```json { "require": { "ext-pdo_pgsql": "*", "ext-gd": "*", "ext-intl": "*" } } ``` ### Web Root Detection migetpacks automatically detects the web root directory by checking for these directories (in order): 1. `web/` 2. `public/` 3. `html/` 4. `www/` 5. `.` (project root, fallback) ## Run Command The default run command uses FrankenPHP with Caddy: ```bash frankenphp run --config /etc/caddy/Caddyfile ``` Override with `RUN_COMMAND` or a `Procfile`: ```text web: frankenphp run --config /etc/caddy/Caddyfile ``` ### PHP Configuration Production PHP settings are automatically configured via environment variables: | Setting | Default | |---------|---------| | `memory_limit` | `256M` | | `max_execution_time` | `30` | | `upload_max_filesize` | `32M` | | `opcache.enable` | `1` | | `display_errors` | `Off` | | `session.cookie_secure` | `1` | These can be overridden at runtime by setting the corresponding `PHP_INI_*` environment variable (e.g., `PHP_INI_MEMORY_LIMIT=512M`). ## Caching ### Layer Caching Dependencies are cached by copying `composer.json` and `composer.lock` before the full source: | Scenario | Dependency Install | |----------|-------------------| | First build | Runs | | Source code change only | **Cached** | | Lockfile change | Runs | ### BUILD_CACHE_DIR When `BUILD_CACHE_DIR` is set, Composer packages are cached at: | Cache Path | Contents | |------------|----------| | `/cache/composer` | Composer package cache | ## DHI Support Docker Hardened Images (DHI) are **not supported** for PHP. FrankenPHP is a specialized application server that requires its own base image. When `USE_DHI=true` is set, migetpacks will continue to use the standard FrankenPHP images. ## Example ```bash docker run --rm \ -v /path/to/php-app:/workspace/source \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=my-php-app:latest \ miget/migetpacks:latest ``` ### With custom environment variables ```bash docker run --rm \ -v /path/to/php-app:/workspace/source \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=my-php-app:latest \ -e PHP_INI_MEMORY_LIMIT=512M \ -e APP_ENV=production \ miget/migetpacks:latest ``` --- url: https://migetpacks.com/languages/python --- --- title: "Python" description: "Build and deploy Python applications with pip or uv, including Django and FastAPI support." --- ## Detection migetpacks detects Python when any of these files are present in your project root: - `requirements.txt` - `setup.py` - `Pipfile` - `pyproject.toml` ## Version Detection Python version is resolved in this order: | Priority | Source | Example | |----------|--------|---------| | 1 | `PYTHON_VERSION` env var | `PYTHON_VERSION=3.12` | | 2 | `.python-version` file | `3.12.8` | | 3 | `runtime.txt` file | `python-3.12.8` | | 4 | `requires-python` in `uv.lock` | `requires-python = ">=3.13"` | | 5 | `requires-python` in `pyproject.toml` | `requires-python = ">=3.12"` | | 6 | Default | `3.12.8` | Version constraints are normalized for Docker compatibility (e.g., `>=3.13` becomes `3.13`). ## Build Process migetpacks generates a multi-stage Dockerfile with virtualenv isolation: ### Standard pip/pipenv/poetry ```dockerfile # Builder stage FROM python:3.12 AS builder WORKDIR /build # Layer 1: Copy dependency files COPY requirements.txt* pyproject.toml* poetry.lock* Pipfile* ./ ENV PYTHONUNBUFFERED=1 # Layer 2: Create virtualenv RUN python -m venv /opt/venv \ && /opt/venv/bin/pip install --upgrade pip # Layer 3: Install dependencies (CACHED when only source code changes) RUN . /opt/venv/bin/activate \ && pip install -r requirements.txt # Layer 4: Copy source code COPY . . # Layer 5: Collectstatic and cleanup RUN . /opt/venv/bin/activate \ && python manage.py collectstatic --noinput \ && rm -rf .git test tests __pycache__ .pytest_cache .coverage htmlcov # Runtime stage FROM python:3.12-slim WORKDIR /app COPY --from=builder /opt/venv /opt/venv COPY --from=builder /build /app ENV PATH="/opt/venv/bin:$PATH" ENV PYTHONUNBUFFERED=1 ``` ### uv Projects When `uv.lock` is present, migetpacks uses the `uv` package manager natively: ```dockerfile # Builder stage FROM python:3.13 AS builder WORKDIR /build COPY requirements.txt* pyproject.toml* uv.lock* ./ ENV PYTHONUNBUFFERED=1 # Install uv and sync dependencies RUN pip install uv \ && uv venv /app/.venv \ && UV_PROJECT_ENVIRONMENT=/app/.venv uv sync --locked --no-dev COPY . . # Runtime stage FROM python:3.13-slim WORKDIR /app COPY --from=builder /app/.venv /app/.venv COPY --from=builder /build /app ENV PATH="/app/.venv/bin:$PATH" ``` ### Package Manager Detection | File | Package Manager | |------|-----------------| | `uv.lock` | uv (native) | | `requirements.txt` | pip | | `Pipfile` / `Pipfile.lock` | pipenv | | `poetry.lock` | poetry | | `pyproject.toml` (no lock) | pip | ### Dependency Detection migetpacks automatically detects database and XML libraries from your dependency files and installs the required system packages: | Python Package | System Package | |---------------|---------------| | `psycopg`, `asyncpg`, `aiopg` | `libpq5` | | `mysqlclient`, `pymysql`, `aiomysql` | `libmariadb3` | | `aiosqlite` | `libsqlite3-0` | | `lxml` | `libxml2`, `libxslt1.1` | ### Runtime Cleanup The following are removed from the final image: - `.git/`, `.github/` - `test/`, `tests/` - `__pycache__/`, `.pytest_cache/` - `.coverage`, `htmlcov/` ### Django Support If `manage.py` is detected, `python manage.py collectstatic --noinput` is automatically run during the build. ## Run Command The default run command is determined in this order: | Priority | Source | Command | |----------|--------|---------| | 1 | `RUN_COMMAND` env var | User-specified | | 2 | `web:` in Procfile | From Procfile | | 3 | Django + gunicorn | `gunicorn project.wsgi:application --bind 0.0.0.0:$PORT` | | 4 | Django + uvicorn | `uvicorn project.asgi:application --host 0.0.0.0 --port $PORT` | | 5 | gunicorn detected | `gunicorn app:app --bind 0.0.0.0:$PORT` | | 6 | uvicorn detected | `uvicorn main:app --host 0.0.0.0 --port $PORT` | | 7 | `manage.py` exists | `python manage.py runserver 0.0.0.0:$PORT` | | 8 | Default | `python app.py` or `python main.py` | ## Caching ### Docker Layer Caching Dependencies are installed in a separate layer using a virtualenv. `pip install` is only re-run when dependency files change. ### BuildKit Cache Mounts When `BUILD_CACHE_DIR` is configured, BuildKit cache mounts are used: | Cache Path | Contents | |-----------|----------| | `/cache/pip` | pip wheel cache | | `/cache/uv` | uv package cache | ### Registry Cache Use `CACHE_IMAGE` to push/pull BuildKit inline cache layers to a registry for cross-build caching. ## DHI Support Python is fully supported with Docker Hardened Images. | Stage | Image | |-------|-------| | Build | `dhi.io/python:{version}-dev` | | Runtime | `dhi.io/python:{version}` | The `-dev` variant includes a shell and build tools. The runtime image is distroless with no shell, providing a minimal attack surface. The virtualenv is copied from the builder stage to the distroless runtime, ensuring all installed packages are available without needing pip in the runtime image. ## Example ```bash docker run --rm \ -v /path/to/app:/workspace/source \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=my-python-app:latest \ miget/migetpacks:latest ``` ### With Custom Options ```bash docker run --rm \ -v /path/to/app:/workspace/source \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=registry.io/app:v1 \ -e PYTHON_VERSION=3.13 \ -e RUN_COMMAND="gunicorn myapp.wsgi:application --bind 0.0.0.0:5000" \ -e USE_DHI=true \ miget/migetpacks:latest ``` --- url: https://migetpacks.com/languages/ruby --- --- title: "Ruby" description: "Build and deploy Ruby and Rails applications with native gem support and asset precompilation." --- ## Detection migetpacks detects Ruby when any of these files are present in your project root: - `Gemfile` - `Rakefile` - `config.ru` ## Version Detection Ruby version is resolved in this order: | Priority | Source | Example | |----------|--------|---------| | 1 | `RUBY_VERSION` env var | `RUBY_VERSION=3.3` | | 2 | `.ruby-version` file | `3.4.4` | | 3 | `ruby` declaration in `Gemfile` | `ruby "3.3.0"` | | 4 | Default | `3.4.4` | The `ruby-` prefix is automatically stripped (e.g., `ruby-3.2.0` becomes `3.2.0`). ## Build Process migetpacks generates a multi-stage Dockerfile with bundler layer caching: ```dockerfile # Builder stage FROM ruby:3.4 AS builder WORKDIR /build # Layer 1: Copy Gemfile for layer caching COPY Gemfile Gemfile.lock ./ # Layer 2: Configure and install gems (CACHED when Gemfile unchanged) ENV GEM_HOME=/build/vendor/bundle ENV BUNDLE_PATH=/build/vendor/bundle RUN gem install bundler -v '2.5.0' --no-document && \ bundle config set --local path '/build/vendor/bundle' && \ bundle config set --local deployment 'true' && \ bundle install --jobs 4 --retry 3 # Layer 3: Copy source code COPY . . # Layer 4: Rails asset precompilation and cleanup ENV RAILS_ENV=production ENV SECRET_KEY_BASE_DUMMY=1 RUN bundle exec rake assets:precompile && \ bundle exec rake assets:clean && \ rm -rf .git test spec vendor/bundle/ruby/*/cache # Runtime stage FROM ruby:3.4-slim RUN apt-get update && apt-get install -y libpq5 tzdata ca-certificates WORKDIR /app COPY --from=builder /build /app ``` ### Dependency Detection migetpacks inspects your `Gemfile` and `Gemfile.lock` to install the correct system libraries: | Gem | Build Package | Runtime Package | |-----|--------------|----------------| | `pg` | `libpq-dev` | `libpq5` | | `mysql2`, `trilogy` | `libmariadb-dev` | `libmariadb3` | | `sqlite3` | `libsqlite3-dev` | `libsqlite3-0` | | `nokogiri`, `rails` | `libxml2-dev`, `libxslt1-dev` | `libxml2`, `libxslt1.1` | Additionally, `tzdata` and `ca-certificates` are always installed in the runtime image. ### Bundler Version The bundler version is automatically detected from `Gemfile.lock` (the `BUNDLED WITH` section) and installed in the builder stage. ### Local Gems If your `Gemfile` references local gems with `path:` options, those directories are automatically copied before `bundle install` to ensure they are available during gem resolution. ### Rails Asset Precompilation If your project has a `Rakefile` and either `app/assets/` or `app/javascript/` directories, migetpacks automatically: 1. Sets `RAILS_ENV=production`, `SECRET_KEY_BASE_DUMMY=1`, and a dummy `DATABASE_URL` 2. Runs `bundle exec rake assets:precompile` 3. Runs `bundle exec rake assets:clean` ### Runtime Cleanup The following are removed from the final image: - `.git/`, `.github/` - `test/`, `tests/`, `spec/`, `features/` - `.rspec`, `.rubocop*` - `vendor/bundle/ruby/*/cache/` ## Run Command The default run command is determined in this order: | Priority | Source | Command | |----------|--------|---------| | 1 | `RUN_COMMAND` env var | User-specified | | 2 | `web:` in Procfile | From Procfile | | 3 | `config.ru` exists | `bundle exec puma -C config/puma.rb` | | 4 | Default | `bundle exec ruby app.rb` | ### Runtime Environment The following environment variables are set in the runtime container: ```bash RAILS_ENV=production RACK_ENV=production RAILS_SERVE_STATIC_FILES=enabled RAILS_LOG_TO_STDOUT=enabled MALLOC_ARENA_MAX=2 DISABLE_SPRING=1 PUMA_PERSISTENT_TIMEOUT=95 ``` ## Caching ### Docker Layer Caching Gems are installed in a separate layer before source code is copied. `bundle install` is only re-run when `Gemfile` or `Gemfile.lock` changes. ### BuildKit Cache Mounts When `BUILD_CACHE_DIR` is configured, BuildKit cache mounts are used: | Cache Path | Contents | |-----------|----------| | `/cache/bundler` | Bundler gem cache | ### Registry Cache Use `CACHE_IMAGE` to push/pull BuildKit inline cache layers to a registry for cross-build caching. ## DHI Support Ruby is fully supported with Docker Hardened Images. | Stage | Image | |-------|-------| | Build | `dhi.io/ruby:{version}-dev` | | Runtime | `dhi.io/ruby:{version}` | ### DHI-Specific Behavior The DHI runtime is distroless (no shell, no apt-get), which requires special handling: **Native gem libraries** are copied from the builder stage: ```dockerfile COPY --from=builder /usr/lib/*/libpq.so* /usr/lib/ COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo COPY --from=builder /etc/ssl/certs /etc/ssl/certs ``` **Process commands are transformed** since shebangs do not work in distroless: - `./bin/rails server` becomes `ruby bin/rails server` - `bundle exec` commands work because bundler is installed as a gem **BUNDLER_VERSION** is explicitly set in the environment to prevent bundler from attempting auto-switching (which fails without `/usr/bin/env`). Shell-dependent features like `.profile.d` scripts and shell variable expansion (`${PORT:-5000}`) are not available in DHI runtime images. Use environment variables directly. ## Multi-Buildpack Ruby applications commonly use Node.js for asset compilation. When `package.json` is detected alongside a Ruby project, Node.js is automatically added as a secondary buildpack: ```bash # Explicit buildpack ordering docker run --rm \ -v /path/to/app:/workspace/source \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=my-rails-app:latest \ -e BUILDPACKS=ruby,nodejs \ miget/migetpacks:latest ``` See the [Multi-Buildpack guide](/guides/multi-buildpack) for more details. ## Example ```bash docker run --rm \ -v /path/to/app:/workspace/source \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=my-ruby-app:latest \ miget/migetpacks:latest ``` ### With Custom Options ```bash docker run --rm \ -v /path/to/app:/workspace/source \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=registry.io/app:v1 \ -e RUBY_VERSION=3.3 \ -e RAILS_MASTER_KEY=abc123 \ -e BUNDLE_WITHOUT="development test" \ -e USE_DHI=true \ miget/migetpacks:latest ``` --- url: https://migetpacks.com/languages/rust --- --- title: "Rust" description: "Build and deploy Rust applications with optimized dependency caching and release binaries." --- ## Detection migetpacks detects Rust when any of these files are present in your project root: - `Cargo.toml` - `Cargo.lock` ## Version Detection Rust version is resolved in this order: | Priority | Source | Example | |----------|--------|---------| | 1 | `RUSTUP_TOOLCHAIN` env var | `RUSTUP_TOOLCHAIN=1.80.0` | | 2 | `rust-toolchain` file | `1.92.0` | | 3 | `channel` in `rust-toolchain.toml` | `channel = "1.92.0"` | | 4 | Default | `1.92.0` | ## Build Process migetpacks generates a multi-stage Dockerfile with an optimized dependency caching strategy: ```dockerfile # Builder stage FROM rust:1.92 AS builder WORKDIR /build # Rust build environment ENV CARGO_HOME=/usr/local/cargo ENV RUSTUP_HOME=/usr/local/rustup # Layer 1: Copy dependency manifests COPY Cargo.toml Cargo.lock* ./ # Layer 2: Create dummy source and build deps (CACHED when Cargo.toml unchanged) RUN mkdir -p src && echo 'fn main() {}' > src/main.rs RUN cargo build --release && rm -rf src # Layer 3: Copy real source code COPY . . # Layer 4: Touch main.rs and rebuild (only app code recompiles) RUN touch src/main.rs RUN cargo build --release \ && cp target/release/myapp /build/app \ && rm -rf src target Cargo.toml Cargo.lock .git # Runtime stage FROM debian:bookworm-slim RUN apt-get update && apt-get install -y ca-certificates WORKDIR /app COPY --from=builder /build /app ENV RUST_BACKTRACE=1 ENV RUST_LOG=info ``` ### Dependency Caching Strategy Rust's dependency caching uses a "dummy source" technique: 1. Copy `Cargo.toml` and `Cargo.lock` 2. Create a minimal `fn main() {}` source file 3. Run `cargo build --release` to compile all dependencies 4. Remove the dummy source 5. Copy the real source code 6. Touch `src/main.rs` to invalidate only the application crate 7. Rebuild -- only the application code recompiles This ensures that dependency compilation is cached across builds when only source code changes. ### Binary Detection The binary name is automatically detected from `Cargo.toml`: 1. `[[bin]]` section `name` field 2. `[package]` section `name` field 3. Falls back to `app` ### Workspace Projects For workspace projects (when `[workspace]` is present in `Cargo.toml`), all workspace member `Cargo.toml` files are copied for proper dependency resolution. ### Static Assets If your project contains `static/`, `public/`, `assets/`, `templates/`, `dist/`, or `build/` directories, they are automatically copied alongside the binary to the runtime image. ## Run Command The default run command is determined in this order: | Priority | Source | Command | |----------|--------|---------| | 1 | `RUN_COMMAND` env var | User-specified | | 2 | `web:` in Procfile | From Procfile | | 3 | Default | `./app` | Procfile commands referencing `./target/release/binary` are automatically translated to `./app` since the binary is copied to the application root during build. ### Runtime Environment The following environment variables are set in the runtime container: ```bash RUST_BACKTRACE=1 RUST_LOG=info ``` ## Caching ### Docker Layer Caching The dummy-source technique ensures that `cargo build --release` for dependencies is cached when only source code changes. A full dependency rebuild only happens when `Cargo.toml` or `Cargo.lock` is modified. ### BuildKit Cache Mounts When `BUILD_CACHE_DIR` is configured, BuildKit cache mounts are used: | Cache Path | Contents | |-----------|----------| | `/cache/cargo` | Cargo registry (`/usr/local/cargo/registry`) and build artifacts (`/build/target`) | ### Registry Cache Use `CACHE_IMAGE` to push/pull BuildKit inline cache layers to a registry for cross-build caching. ## DHI Support Rust is fully supported with Docker Hardened Images. | Stage | Image | |-------|-------| | Build | `dhi.io/rust:{version}-dev` | | Runtime | `dhi.io/rust:{version}` | The `-dev` variant includes a shell and build tools (cargo, rustc). The runtime image is distroless with no shell, providing a minimal attack surface. Since Rust produces static binaries, the distroless runtime works without any special handling. ## Example ```bash docker run --rm \ -v /path/to/app:/workspace/source \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=my-rust-app:latest \ miget/migetpacks:latest ``` ### With Custom Options ```bash docker run --rm \ -v /path/to/app:/workspace/source \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=registry.io/app:v1 \ -e RUSTUP_TOOLCHAIN=1.80.0 \ -e BUILD_COMMAND="cargo build --release --features production" \ -e USE_DHI=true \ miget/migetpacks:latest ``` ### Workspace Project ```bash docker run --rm \ -v /path/to/workspace:/workspace/source \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=registry.io/api:v1 \ -e RUN_COMMAND="./app" \ miget/migetpacks:latest ``` --- url: https://migetpacks.com/languages/scala --- --- title: "Scala" description: "Build and deploy Scala applications with migetpacks using sbt." --- ## Detection migetpacks detects a Scala application when any of the following are present in the project root: - `build.sbt` - `build.sc` - `project/` directory ## Version Detection ### Scala Version Scala version is resolved in the following priority order: | Source | Example | |--------|---------| | `.scala-version` | `3.5.2` | | `build.sbt` (`scalaVersion`) | `scalaVersion := "3.5.2"` | | `project/build.properties` (`scala.version`) | `scala.version=3.5.2` | | Default | `3.5.2` | ### sbt Version sbt version is detected from: | Source | Example | |--------|---------| | `project/build.properties` | `sbt.version=1.10.6` | | Default | `1.10.6` | sbt 0.13.x is deprecated and will show a warning. sbt versions below 0.13.18 are not supported. sbt 2.x support is experimental. ### Java Version The JDK version used for compilation and runtime is read from: | Source | Example | |--------|---------| | `system.properties` | `java.runtime.version=21` | | Default | `21` | Supported Java versions: 8, 11, 17, 21. ## Build Process migetpacks uses the official `sbtscala/scala-sbt` Docker image which includes sbt, Scala, and the JDK: ```dockerfile # Build stage FROM sbtscala/scala-sbt:eclipse-temurin-21.0.6_7_1.11.7_3.5.2 AS builder WORKDIR /build # Scala/sbt build environment ENV JAVA_TOOL_OPTIONS="-Dfile.encoding=UTF-8" ENV SBT_OPTS="-Xmx2G -XX:MaxMetaspaceSize=512m" # Layer caching: copy sbt build files first COPY build.sbt ./ COPY project/ project/ # Download dependencies (cached if build files unchanged) RUN sbt update || true # Copy source code COPY . . # Build and cleanup RUN sbt compile assembly \ && rm -rf src/ project/ build.sbt # Runtime stage FROM eclipse-temurin:21-jre WORKDIR /app COPY --from=builder --chown=1000:1000 /build /app ``` ### Framework Detection migetpacks detects the application type and adjusts the build accordingly: | Detection | Build Command | Output | |-----------|--------------|--------| | Play Framework (`PlayScala`/`PlayJava` in `build.sbt`) | `sbt compile stage` | `target/universal/stage/` | | sbt-native-packager (in `project/plugins.sbt`) | `sbt compile stage` | `target/universal/stage/` | | Standard sbt project | `sbt compile assembly` | `target/scala-*/app.jar` | ## Run Command The default run command depends on the project type: | Project Type | Default Command | |-------------|----------------| | Standard (assembly) | `java -jar target/scala-*/app.jar` | | Play Framework | `target/universal/stage/bin/{app-name}` | | sbt-native-packager | `target/universal/stage/bin/{app-name}` | Override with `RUN_COMMAND` or a `Procfile`: ```text web: java -jar target/scala-3.5.2/myapp-assembly-1.0.jar ``` ### JVM Configuration The following environment variables are set for production: | Variable | Value | |----------|-------| | `JAVA_OPTS` | `-Dfile.encoding=UTF-8 -XX:MaxRAMPercentage=80.0` | | `JAVA_TOOL_OPTIONS` | `-Dfile.encoding=UTF-8` | ## Caching ### Layer Caching Dependencies are cached by copying sbt build definition files before the full source: | Cached Files | Purpose | |-------------|---------| | `build.sbt` | Project build definition | | `project/` | sbt plugins, build properties, Scala version | | Scenario | Dependency Install | |----------|-------------------| | First build | Runs | | Source code change only | **Cached** | | Build file change | Runs | ### BUILD_CACHE_DIR When `BUILD_CACHE_DIR` is set, sbt and dependency caches persist at: | Cache Path | Contents | |------------|----------| | sbt cache | sbt boot and global settings | | ivy cache | Ivy repository (legacy dependencies) | | coursier cache | Coursier downloads (modern dependencies) | ## DHI Support Docker Hardened Images are supported for Scala runtime. Scala uses a hybrid approach: the official `sbtscala` build image (which includes sbt tools) for building, and a DHI distroless image for runtime. | Stage | Image | |-------|-------| | Build | `sbtscala/scala-sbt:eclipse-temurin-{jdk}_{sbt}_{scala}` (official) | | Runtime | `dhi.io/eclipse-temurin:{java}` | ### DHI Notes - The build image is always the official sbtscala image (DHI does not provide sbt images). - Only the runtime stage uses DHI distroless Eclipse Temurin JRE. - Java version is read from `system.properties` (default: 21). - Runtime images are distroless (no shell). ### Example with DHI ```bash docker run --rm \ -v /path/to/scala-app:/workspace/source \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=my-scala-app:latest \ -e USE_DHI=true \ miget/migetpacks:latest ``` ## Example ```bash docker run --rm \ -v /path/to/scala-app:/workspace/source \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=my-scala-app:latest \ miget/migetpacks:latest ``` ### Play Framework app ```bash docker run --rm \ -v /path/to/play-app:/workspace/source \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=my-play-app:latest \ -e RUN_COMMAND="target/universal/stage/bin/myapp -Dhttp.port=5000" \ miget/migetpacks:latest ``` --- url: https://migetpacks.com/quickstart --- --- title: "Quickstart" description: "Build your first container in 3 commands" --- ## Prerequisites You need Docker installed and running on your machine. Any version with BuildKit support (18.09+) works. ## Build Your First Container Download the latest migetpacks image from Docker Hub: ```bash docker pull miget/migetpacks:latest ``` Point migetpacks at your application source code. It will auto-detect the language, version, and build configuration: ```bash docker run --rm \ -v /path/to/your/app:/workspace/source \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=my-app:local \ miget/migetpacks:latest ``` Replace `/path/to/your/app` with the absolute path to your project directory. When `OUTPUT_IMAGE` does not contain a registry prefix (e.g., `my-app:local` instead of `registry.io/my-app:latest`), the image stays on your local Docker daemon and is not pushed anywhere. Run your newly built container: ```bash docker run --rm -p 5000:5000 my-app:local ``` Open [http://localhost:5000](http://localhost:5000) in your browser to see your app running. ## What Happened? When you ran migetpacks, it performed four steps automatically: Scanned your source code for language markers (`package.json`, `Gemfile`, `go.mod`, `requirements.txt`, etc.) and identified the runtime. Read version files (`.nvmrc`, `.ruby-version`, `.python-version`, `go.mod`) and lock files to determine the exact runtime version. Generated an optimized multi-stage Dockerfile with dependency layer caching, build commands, and a minimal runtime stage. Used Docker-in-Docker with BuildKit to build the image and load it into your local Docker daemon. ## Common Options ```bash Specify Language docker run --rm \ -v /path/to/your/app:/workspace/source \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=my-app:local \ -e LANGUAGE=ruby \ miget/migetpacks:latest ``` ```bash Custom Port docker run --rm \ -v /path/to/your/app:/workspace/source \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=my-app:local \ -e PORT=3000 \ miget/migetpacks:latest ``` ```bash Custom Run Command docker run --rm \ -v /path/to/your/app:/workspace/source \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=my-app:local \ -e RUN_COMMAND="bundle exec puma -C config/puma.rb" \ miget/migetpacks:latest ``` ```bash Hardened Images (DHI) docker run --rm \ -v /path/to/your/app:/workspace/source \ -v /var/run/docker.sock:/var/run/docker.sock \ -e OUTPUT_IMAGE=my-app:local \ -e USE_DHI=true \ miget/migetpacks:latest ``` ## Push to a Registry To push the built image to a remote registry, use a fully-qualified image name and mount your Docker credentials: ```bash docker run --rm \ -v /path/to/your/app:/workspace/source \ -v /var/run/docker.sock:/var/run/docker.sock \ -v $HOME/.docker/config.json:/root/.docker/config.json:ro \ -e OUTPUT_IMAGE=registry.io/myorg/my-app:latest \ miget/migetpacks:latest ``` ## Next Steps Understand the detection, generation, and build pipeline in detail. Full reference for all configuration options. Set up migetpacks in your CI/CD pipeline. Use distroless, CVE-free base images for production. --- url: https://migetpacks.com/reference/architecture --- --- title: "Architecture" description: "How migetpacks builds containers" --- ## Overview migetpacks is a containerized build runtime that detects your application's language, generates an optimized multi-stage Dockerfile, and builds a production-ready container image. It runs entirely inside a Docker container using Docker-in-Docker (DinD). ## Build Flow The build process follows a linear pipeline: ```text detect → detect-version → generate Dockerfile → buildx build → push ``` 1. **Detect** (`bin/detect`) -- Identifies the programming language from source files 2. **Detect Version** (`bin/detect-version`) -- Reads version from `.node-version`, `.nvmrc`, `.ruby-version`, `package.json`, etc. 3. **Generate Dockerfile** (`bin/build`) -- Creates a multi-stage Dockerfile using the language-specific library 4. **Build** -- Runs `docker buildx build` with BuildKit features 5. **Push** -- Pushes the built image to the target registry (or loads locally) ## Docker-in-Docker (DinD) migetpacks starts a Docker daemon (`dockerd`) inside its own container. This provides: - Isolation from the host Docker daemon - Control over BuildKit configuration - Registry mirror configuration - Custom storage driver options ### Daemon Configuration The internal Docker daemon is configured with: ```json { "storage-driver": "overlay2", "registry-mirrors": ["https://your-mirror.io"], "features": { "buildkit": true } } ``` ### MTU Configuration The Docker network MTU is lowered by 50 bytes from the host MTU to accommodate overlay network encapsulation headers: ```text Host MTU: 1500 DinD MTU: 1450 ``` This prevents packet fragmentation when running inside Kubernetes overlay networks (Flannel, Calico, Weave). ### Storage Driver The default storage driver is `overlay2`. For nested DinD environments where `overlay2` is not available, use `fuse-overlayfs`: ```bash STORAGE_DRIVER=fuse-overlayfs ``` ## Multi-Stage Builds Every generated Dockerfile uses multi-stage builds to separate build dependencies from the runtime image: ```dockerfile # Stage 1: Builder (has compilers, dev tools, build deps) FROM node:20 AS builder WORKDIR /build COPY package.json package-lock.json ./ RUN npm ci COPY . . RUN npm run build && npm prune --production # Stage 2: Runtime (minimal, no build tools) FROM node:20-slim WORKDIR /app COPY --from=builder /build ./ USER miget EXPOSE 5000 CMD ["node", "server.js"] ``` **Benefits:** - Runtime image contains only production dependencies - Build tools and intermediate files are not shipped - Smaller image size and reduced attack surface ## BuildKit Features migetpacks leverages BuildKit for advanced build capabilities: ### Cache Mounts Package manager caches are mounted as BuildKit cache volumes, persisting across builds: ```dockerfile RUN --mount=type=cache,target=/root/.npm,sharing=shared \ npm ci ``` This avoids re-downloading packages when only source code changes. ### Inline Cache When `CACHE_IMAGE` is set, BuildKit exports cache metadata inline with the image, enabling cross-machine cache reuse: ```bash CACHE_IMAGE=registry.io/myapp:cache CACHE_MODE=max # Cache all layers (default: min, final layer only) ``` ### Additional Cache Sources Use `CACHE_FROM` to specify additional read-only cache sources: ```bash CACHE_FROM=registry.io/myapp:main-cache,registry.io/myapp:develop-cache ``` These are skipped when `NO_CACHE=true`. ## Layer Caching Strategy Dockerfiles are generated with optimized layer ordering to maximize cache hits: ```dockerfile # Layer 1: Dependency manifest (changes rarely) COPY package.json package-lock.json ./ # Layer 2: Install dependencies (CACHED if manifest unchanged) RUN npm ci # Layer 3: Source code (changes frequently) COPY . . # Layer 4: Build (runs when source changes) RUN npm run build ``` | Scenario | Layer 2 (install) | Layer 4 (build) | |----------|-------------------|-----------------| | First build | Runs | Runs | | Source change only | **Cached** | Runs | | Lockfile change | Runs | Runs | This optimization means dependency installation is skipped for most iterative builds, saving significant time. ## Registry Mirror When `REGISTRY_MIRROR` is set, it is configured in the Docker daemon's `registry-mirrors` list. All image pulls automatically try the mirror first before falling back to Docker Hub: ```json { "registry-mirrors": ["https://registry.example.io/mirror"] } ``` This is particularly effective for: - Avoiding Docker Hub rate limits - Reducing pull times in private networks - Caching base images locally (e.g., Harbor pull-through proxy) ## Security Model ### Non-Root Runtime All generated containers run as a non-root user (`miget` uid 1000, or `nonroot` for DHI). This prevents privilege escalation and meets Kubernetes Pod Security Standards. ### Read-Only Source Directories Source code is mounted read-only (`readOnly: true`) when possible. migetpacks copies source to a working directory before building, so the original source is never modified. ### Credential Filtering The `KNOWN_BUILDER_VARS` list in `bin/build` ensures sensitive environment variables are never written to the generated Dockerfile: - `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_SESSION_TOKEN` -- never exposed - `DOCKER_CONFIG` -- not written to image - Builder-internal variables -- filtered out Only unknown environment variables (user-defined) are injected as `ENV` statements in the Dockerfile. This enables passing build-time configuration like `NODE_OPTIONS` or `VITE_API_URL` while keeping secrets safe. ### Image Provenance BuildKit records build provenance metadata, including: - Source commit information (when available) - Build timestamps - Builder image version ## Component Reference | Component | Path | Purpose | |-----------|------|---------| | Language detection | `bin/detect` | Identifies language from source files | | Version detection | `bin/detect-version` | Reads version from config files | | Main build orchestrator | `bin/build` | Generates Dockerfile, runs buildx | | Release metadata | `bin/release` | Parses Procfile, outputs process types | | Common helpers | `lib/common.sh` | Output formatting, cache ID generation | | Multi-buildpack | `lib/buildpacks.sh` | Multiple language runtime support | | Node.js | `lib/nodejs.sh` | Node.js/npm/yarn/pnpm builds | | Python | `lib/python.sh` | Python/pip/uv builds | | Ruby | `lib/ruby.sh` | Ruby/Bundler/Rails builds | | Go | `lib/go.sh` | Go module builds | | Rust | `lib/rust.sh` | Rust/Cargo builds | | Java | `lib/java.sh` | Java/Maven/Gradle builds | | Kotlin | `lib/kotlin.sh` | Kotlin/Gradle builds | | Scala | `lib/scala.sh` | Scala/sbt builds | | Clojure | `lib/clojure.sh` | Clojure/Leiningen builds | | .NET | `lib/dotnet.sh` | .NET/C# builds | | PHP | `lib/php.sh` | PHP/Composer/FrankenPHP builds | | Elixir | `lib/elixir.sh` | Elixir/Phoenix builds | | Deno | `lib/deno.sh` | Deno builds | | Bun | `lib/bun.sh` | Bun builds | | Entrypoint | `bin/entrypoint.sh` | Container entrypoint (starts dockerd) | --- url: https://migetpacks.com/reference/result-json --- --- title: "Result JSON" description: "Build output schema reference" --- ## Overview When `RESULT_FILE` is set, migetpacks writes a JSON file containing build metadata after every build. This file is used by post-build steps (e.g., deployment pipelines, notification services) to determine build status and access image information. The builder always exits with code 0 to ensure post-build steps (like Shipwright callbacks or sidecar containers) always execute. Check the `status` field in the result JSON to determine if the build actually succeeded or failed. ## Schema | Field | Type | Description | |-------|------|-------------| | `status` | `"success"` or `"failed"` | Build outcome | | `exit_code` | number | Docker build exit code (0 on success) | | `error` | string | Error message (only present on failure) | | `images` | array | Built images with detected ports | | `processes` | object | Process types from Procfile | | `env` | object | Environment variables from app.json | | `formation` | array | Process formation from app.json | | `scripts` | array | Deployment scripts from app.json | | `addons` | array | Service dependencies from app.json | | `build_time` | number | Build duration in seconds | | `language` | string | Detected or specified language | | `timestamp` | string | ISO 8601 build completion time | | `_shell` | boolean | `false` for distroless/DHI containers (no shell available) | | `build` | object | Git provenance and builder facts (commit, branch, description, repository, build time, builder version) | ### images ```json { "images": [ { "name": "registry.io/your-org/your-app:latest", "ports": ["5000/tcp"] } ] } ``` For Docker Compose builds, multiple images are listed: ```json { "images": [ {"name": "registry.io/myapp-api:latest", "ports": ["5000/tcp"]}, {"name": "registry.io/myapp-web:latest", "ports": ["3000/tcp"]} ] } ``` ### processes Parsed from the application's Procfile: ```json { "processes": { "web": "bundle exec puma -C config/puma.rb", "release": "bundle exec rails db:migrate", "workers": [ {"name": "sidekiq", "command": "bundle exec sidekiq"}, {"name": "clock", "command": "bundle exec clockwork clock.rb"} ] } } ``` - `web` -- the primary web process (from `RUN_COMMAND`, Procfile `web:`, or language default) - `release` -- one-time release command (e.g., database migrations) - `workers` -- background worker processes (all non-web, non-release Procfile entries) ### env Environment variables defined in `app.json`: ```json { "env": { "SECRET_KEY_BASE": "a1b2c3d4e5f6...", "RAILS_ENV": "production", "DATABASE_URL": null } } ``` - Variables with `"generator": "secret"` have auto-generated values - Variables with `"value": "..."` use the specified value - Variables with `"required": true` are `null` (must be provided at deploy time) ### formation Process configuration from `app.json`: ```json { "formation": [ {"name": "web", "quantity": 2, "size": "standard-1x"}, {"name": "worker", "quantity": 1, "size": null} ] } ``` ### scripts Deployment scripts from `app.json`: ```json { "scripts": [ {"name": "postdeploy", "command": "bundle exec rails db:migrate"}, {"name": "pr-predestroy", "command": "bundle exec rails db:drop"} ] } ``` ### addons Service dependencies from `app.json`: ```json { "addons": [ {"plan": "heroku-postgresql:hobby-dev", "as": null, "options": null}, {"plan": "heroku-redis:premium-0", "as": "CACHE", "options": null}, {"plan": "mongolab:shared-single-small", "as": "MONGO", "options": {"version": "5.0"}} ] } ``` - `plan` -- the addon plan identifier - `as` -- attachment alias (overrides config var prefix), or `null` - `options` -- provider-specific options object, or `null` ### _shell Indicates whether the runtime container has a shell available: ```json { "_shell": false } ``` When `_shell` is `false` (DHI/distroless images), deployment systems should: - Use exec format for commands (no `/bin/sh -c` wrapper) - Pre-expand shell variables like `${PORT:-5000}` - Avoid relying on shebangs in scripts ### build Git provenance and builder facts, present on every build: ```json { "build": { "commit": "9f3c1a7b8e2d4f5a6c7b8e2d4f5a6c7b8e2d4f5a", "commit_short": "9f3c1a7", "branch": "main", "description": "Fix build-vars call order", "committed_at": "2026-06-17T19:40:00Z", "built_at": "2026-06-17T20:00:00Z", "builder_version": "0.0.264", "language": "ruby", "repository": "https://github.com/acme/my-api" } } ``` Unknown fields are omitted. Values resolve by precedence: the `build-meta` input envelope, then `MIGET_GIT_*` / `SOURCE_VERSION` environment variables, then `.git` autodetection of the source directory. ## Success Example A complete result file for a successful Ruby build: ```json { "status": "success", "exit_code": 0, "images": [ { "name": "registry.io/your-org/your-app:latest", "ports": ["5000/tcp"] } ], "processes": { "web": "bundle exec puma -C config/puma.rb", "release": "bundle exec rails db:migrate", "workers": [ {"name": "sidekiq", "command": "bundle exec sidekiq"} ] }, "env": { "SECRET_KEY_BASE": "a1b2c3d4e5f6789...", "RAILS_ENV": "production", "DATABASE_URL": null }, "formation": [ {"name": "web", "quantity": 2, "size": "standard-1x"}, {"name": "worker", "quantity": 1, "size": null} ], "scripts": [ {"name": "postdeploy", "command": "bundle exec rails db:migrate"} ], "addons": [ {"plan": "heroku-postgresql:hobby-dev", "as": null, "options": null} ], "build_time": 45, "language": "ruby", "timestamp": "2025-12-18T20:00:00Z" } ``` ## Failure Example A result file for a failed build: ```json { "status": "failed", "exit_code": 1, "error": "Docker build failed: npm ERR! Missing: peer dependency react@^18.0.0", "images": [], "processes": {}, "build_time": 12, "language": "nodejs", "timestamp": "2025-12-18T20:01:00Z" } ``` ## Usage ### Reading in Shell Scripts ```bash #!/bin/bash RESULT_FILE="/workspace/output/build-result.json" # Wait for result file while [ ! -f "$RESULT_FILE" ]; do sleep 2; done # Check status STATUS=$(jq -r '.status' "$RESULT_FILE") if [ "$STATUS" = "failed" ]; then ERROR=$(jq -r '.error' "$RESULT_FILE") echo "Build failed: $ERROR" exit 1 fi # Get image name IMAGE=$(jq -r '.images[0].name' "$RESULT_FILE") echo "Built image: $IMAGE" ``` ### Reading in Python ```python import json import time from pathlib import Path result_path = Path("/workspace/output/build-result.json") # Wait for result while not result_path.exists(): time.sleep(2) result = json.loads(result_path.read_text()) if result["status"] == "failed": raise Exception(f"Build failed: {result['error']}") image = result["images"][0]["name"] ports = result["images"][0]["ports"] print(f"Built {image} exposing {ports}") ``` ### Shipwright Post-Build Step ```yaml - name: deploy image: your-deployer:latest volumeMounts: - name: results mountPath: /workspace/output env: - name: RESULT_FILE value: /workspace/output/build-result.json ``` --- url: https://migetpacks.com/reference/runtime-container --- --- title: "Runtime Container" description: "Built container configuration" --- ## Overview migetpacks produces optimized runtime containers that run as non-root users by default. The container configuration depends on whether you are using standard images or Docker Hardened Images (DHI). ## Standard Runtime When `USE_DHI=false` (default), migetpacks generates containers with the following configuration: | Property | Value | |----------|-------| | User | `miget` (uid: 1000) | | Group | `miget` (gid: 1000) | | Working directory | `/app` | | Exposed port | `5000` (configurable via `PORT`) | | Shell | Available (`/bin/sh`) | ### Container Structure ```text /app/ # Application source and dependencies /app/node_modules/ # Node.js dependencies (if applicable) /app/vendor/bundle/ # Ruby gems (if applicable) /app/.venv/ # Python virtualenv (if applicable) ``` ### User Configuration The `miget` user is created in the runtime image: ```dockerfile RUN groupadd -g 1000 miget && \ useradd -u 1000 -g miget -m miget USER miget WORKDIR /app ``` ### Port Configuration The default exposed port is `5000`. Override it with the `PORT` environment variable: ```bash docker run --rm \ -e PORT=3000 \ -e OUTPUT_IMAGE=my-app:local \ miget/migetpacks:latest ``` The `PORT` environment variable is set in the generated Dockerfile, so the application can read it at runtime. ## DHI Runtime (Distroless) When `USE_DHI=true`, migetpacks uses Docker Hardened Images from dhi.io. These are distroless containers with a minimal attack surface. | Property | Value | |----------|-------| | User | `nonroot` (uid varies by image) | | Working directory | `/app` | | Exposed port | `5000` (configurable via `PORT`) | | Shell | **Not available** | | Package manager | **Not available** | ### Distroless Constraints DHI runtime images have no shell, no package manager, and no common utilities. This means: 1. **No shell commands** -- Commands cannot use `/bin/sh -c` wrappers 2. **Exec format only** -- Process commands must use exec format 3. **No shebangs** -- Scripts like `./bin/rails` must be invoked as `ruby bin/rails` 4. **No variable expansion** -- Shell variables like `${PORT:-5000}` must be pre-expanded 5. **No debugging tools** -- No `ls`, `cat`, `curl`, etc. in production ### Command Transformation migetpacks automatically transforms commands for distroless: | Original | Transformed | |----------|-------------| | `./bin/rails server` | `ruby bin/rails server` | | `bundle exec puma` | `ruby -S bundle exec puma` | | `${PORT:-5000}` | Pre-expanded to actual port value | ### Result JSON Indicator When DHI is used, the result JSON includes `"_shell": false` to signal that the container is distroless. Deployment systems should use this flag to determine how to execute commands in the container. ## Runtime Cleanup migetpacks automatically removes unnecessary files from runtime images to reduce size and attack surface. ### Node.js | Removed | Description | |---------|-------------| | `.git/` | Git history | | `.github/` | GitHub configuration | | `test/`, `tests/` | Test files | | `spec/` | Spec files | | `__tests__/` | Jest tests | | `coverage/` | Coverage reports | | `.nyc_output/` | NYC coverage data | | `.cache/` | Build cache | ### Ruby | Removed | Description | |---------|-------------| | `.git/` | Git history | | `.github/` | GitHub configuration | | `test/`, `tests/` | Test files | | `spec/` | RSpec files | | `features/` | Cucumber features | | `.rspec` | RSpec config | | `.rubocop*` | Rubocop config | | `vendor/bundle/*/cache/` | Gem cache | ### Python | Removed | Description | |---------|-------------| | `.git/` | Git history | | `.github/` | GitHub configuration | | `test/`, `tests/` | Test files | | `__pycache__/` | Bytecode cache | | `.pytest_cache/` | Pytest cache | | `.coverage` | Coverage data | | `htmlcov/` | Coverage HTML | ### .NET | Removed | Description | |---------|-------------| | `.git/` | Git history | | `.github/` | GitHub configuration | | `*.cs` | C# source files | | `*.csproj` | Project files | | `*.sln`, `*.slnx` | Solution files | | `obj/` | Intermediate build output | | `bin/Release/net*/` | Additional framework outputs | | `bin/Debug/` | Debug builds | .NET is a compiled language, so source code is completely removed from the runtime image. Node.js, Ruby, and Python are interpreted languages, so source code is kept but development/test files are removed. ## Inspecting Built Containers ### List Files ```bash docker run --rm my-app:local ls -la /app/ ``` ### Check Environment ```bash docker run --rm my-app:local env ``` ### Check User ```bash docker run --rm my-app:local id # uid=1000(miget) gid=1000(miget) groups=1000(miget) ``` ### Check Exposed Ports ```bash docker inspect my-app:local --format='{{json .Config.ExposedPorts}}' # {"5000/tcp":{}} ``` ### Check Entrypoint/CMD ```bash docker inspect my-app:local --format='{{json .Config.Cmd}}' # ["node","server.js"] ``` ### Layer History ```bash docker history my-app:local ``` ### Image Size ```bash docker images my-app:local --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}" ``` ## Security ### Non-Root by Default All migetpacks containers run as a non-root user. This provides: - Process isolation from the host - Prevention of privilege escalation - Compliance with Kubernetes Pod Security Standards (restricted profile) ### Read-Only Filesystem For maximum security, run the container with a read-only root filesystem: ```bash docker run --rm \ --read-only \ --tmpfs /tmp \ -p 5000:5000 \ my-app:local ``` ### No Credentials in Image migetpacks never includes AWS credentials or other secrets in the built image. The `KNOWN_BUILDER_VARS` filter ensures sensitive variables (like `AWS_ACCESS_KEY_ID`) are excluded from the generated Dockerfile.