Self-hosted service
chainguard-dev/melange avatar
chainguard-dev/melange

melange turns an APK into a pipeline you write, one step at a time

build APKs from source code

629 stars183 forksGoApache-2.0

At a glance

What is it?
melange builds Alpine style APK packages from declarative YAML pipelines, with QEMU handling multiple architectures and a step model that replaces the fixed phases of a traditional package manager. It is aimed at teams producing container images with apko.
Who is it for?
Adopt melange if you build container images with apko and need packages that upstream Alpine and Wolfi do not ship, or if you want build steps and tests written in one file next to your pipeline. Do not adopt it as a general purpose package manager for a workstation or as a replacement for a distribution's own build system, since it targets the APK ecosystem rather than your platform's native packages.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A build file that is a pipeline, not a phase list

melange builds APK packages using declarative pipelines, and the README states the key feature plainly: every step of the build pipeline is defined and controlled by you, unlike traditional package managers which have distinct phases.

That difference is the whole idea. In a traditional packaging system the phases exist to enforce a fixed order, and your build file slots into them. In melange the order is yours to choose. The pipeline is a list of steps, and each step is either a named pipeline builtin invoked with uses, or a run step that executes shell commands.

The example in the README makes this concrete. The pipeline fetches the GNU hello source tarball with an expected sha256, then runs autoconf/configure, autoconf/make and autoconf/make-install, then strips the binaries. Four or five steps, each one visible, each one yours.

The tool is written in Go and lives in the chainguard-dev organisation. It is most commonly used to provide custom packages for container images built with apko, and the majority of the apks are built for the Wolfi or Alpine Linux ecosystems. The homepage is not given in the repository, and the project documentation lives in the docs/ directory referenced from the README.

The audience is therefore narrow and clear: teams assembling container images who need a package that does not exist yet, and who want the build of that package to live in the same repository as the image definition.

One YAML file holds the package, its environment and its tests

The build file is a single YAML document with named sections. Here is the shape of the GNU hello example, trimmed to the parts that matter for understanding the layout.

yaml
package:
  name: hello
  version: 2.12
  epoch: 0
  description: "the GNU hello world program"

The package section carries identity and metadata, including the version and epoch, and a copyright block with an attestation and a license identifier. It also carries a dependencies section with a runtime list.

yaml
environment:
  contents:
    repositories:
      - https://dl-cdn.alpinelinux.org/alpine/edge/main
    packages:
      - alpine-baselayout-data
      - busybox
      - build-base
      - scanelf
      - ssl_client
      - ca-certificates-bundle

The environment section is the build environment: which repositories to pull from and which packages must be present while the pipeline runs. This is how a build gets a toolchain without that toolchain becoming a runtime dependency of the result.

yaml
pipeline:
  - uses: fetch
    with:
      uri: https://ftp.gnu.org/gnu/hello/hello-${{package.version}}.tar.gz
      expected-sha256: cf04af86dc085268c5f4470fbae49b18afbc221b78096aab842d934a76bad0ab
  - uses: autoconf/configure
  - uses: autoconf/make
  - uses: autoconf/make-install
  - uses: strip

Notice the expected-sha256 on the fetch step. The source is verified against a digest declared in the build file itself, so the provenance of what went into the package is stated in the same document that describes how to build it.

Subpackages, split steps and tests that run in the pipeline

A melange build file can produce more than one package. The subpackages section defines additional outputs with their own metadata, their own dependencies and their own pipeline.

yaml
subpackages:
  - name: "hello-doc"
    description: "Documentation for hello"
    dependencies:
      runtime:
        - foo
    pipeline:
      - uses: split/manpages
    test:
      pipeline:
        - uses: test/docs

So the documentation can be split out of the main package into its own apk with its own runtime dependency list, using a split step. That is the packaging granularity you would expect from a distribution's own build system, expressed in the same file as everything else.

Testing is part of the same document. The example has a test section with its own environment contents and a pipeline that runs the built program and its version flag.

yaml
test:
  environment:
    contents:
      packages:
        - bar
  pipeline:
    - runs: |
        hello
        hello --version

This matters more than it might look. In a conventional packaging flow, testing the built artifact is a separate job wired up outside the package definition, and it drifts from the package. Here the test lives in the same file with the same substitutions available, so the test and the build cannot drift apart.

The examples directory shows how varied these files are: R-build.yaml, aws-sdk-core.yaml, cargo-build.yaml, go-build.yaml, go-install.yaml, npm-install.yaml, pnpm-install.yaml, pipelineless.yaml, one-arch.yaml, conditional.yaml, replacement-provides.yaml, sshfs.yaml and split-bin.yaml, split-lib.yaml.

Substitutions let one build file serve every architecture

The build file is not a template you expand by hand. melange provides default substitutions that a pipeline can reference, and the table in the README lists them.

The package ones give the metadata back: package.name, package.version, package.epoch, package.full-version, which is the version and epoch combined as version-r epoch, and package.description. There is also package.srcdir, the package source directory set with the --source-dir flag.

The target ones describe where things go: targets.outdir, targets.contextdir, targets.destdir for the main package and targets.subpkgdir for subpackages.

The build ones are the reason this table exists. build.arch gives the architecture of the current build, for example x86_64 or aarch64, and build.goarch gives GOARCH of the current build, for example amd64 or arm64. The README shows a pipeline using one.

yaml
pipeline:
  - name: 'Create tmp dir'
    runs: mkdir ${{targets.destdir}}/var/lib/${{package.name}}/tmp

This is what makes multi-architecture builds work. The same file produces a package for every architecture you target, and the substitutions carry the architecture and the package identity into the steps that need them.

And that is also why QEMU appears in the key features. The README states that QEMU is used to emulate various architectures, avoiding the need for cross-compilation steps. Instead of teaching your build to cross-compile for five targets, you run the same build under emulation.

Building it, and the fact that it needs a container runtime

There are three installation routes. Homebrew, from source with go install, and the container image.

shell
brew install melange
shell
go install chainguard.dev/melange@latest
shell
docker run cgr.dev/chainguard/melange version

Then the build itself.

shell
melange build examples/gnu-hello.yaml

Or with Docker, mounting the current directory.

shell
docker run --privileged --rm -v "${PWD}":/work \
  cgr.dev/chainguard/melange build examples/gnu-hello.yaml

The README says running outside of a container requires Docker, and that it should also work with other runtimes such as podman. The container route in the examples is privileged, which tells you something about what the build does: it starts a build environment and runs commands in it, and the isolation it wants is more than an unprivileged container gives you.

That is the cost of the model. In exchange, the build environment is defined in the file rather than in a shell script that assumes things about the host, and every architecture gets the same environment because the environment is a container image.

The output lands in a packages folder with an entry for each architecture the package supports, and inside each architecture directory you find the apk files for each package built in the pipeline. If you only want the current architecture, the README says you can add --arch with the output of uname -m.

Signing your apks with melange keygen

Package signing is a first class command rather than an external tool invocation. You create a signing key with melange keygen, and the README shows what it prints.

shell
melange keygen
code
 generating keypair with a 4096 bit prime, please wait...
 wrote private key to melange.rsa
 wrote public key to melange.rsa.pub

Then you pass the --signing-key argument to melange build.

Two details are worth noting. The key is written into the current directory as melange.rsa and melange.rsa.pub, so it lands in your working tree and has to be kept out of version control and out of any build artefact directory. And the key generation is interactive in the sense that it takes a while, since it is generating a 4096 bit RSA keypair, which is a useful thing to know before you script it in CI and assume it is instant.

The melange.yaml file at the repository root shows that configuration is expected to reference the signing key by path, so the key and the build file are meant to live together in a controlled way rather than being generated on the fly.

Debugging is set -x in a pipeline step

The debugging story is intentionally small. The README says that to include debug level information on a melange build you edit your melange.yaml file and include set -x in your pipeline, and that you can add the flag at any point in your pipeline commands to debug a specific section of the build.

yaml
...
pipeline:
  - name: Build Minicli application
    runs: |
      set -x
      APP_HOME="${{targets.destdir}}/usr/share/hello-minicli"
...

Because the pipeline is just steps, adding tracing is a matter of putting a shell flag in the step you care about. There is no separate verbose logging mode to learn, and no separate debug configuration file. The trace comes from the shell that runs the step, which is why it appears in the build output directly.

That simplicity is a direct consequence of the pipeline model. In a phase based package manager, you often need a separate mechanism to see what a phase did, because the phase is hidden behind tooling. Here the step is a command and the command's own facilities apply to it.

The one cost is that tracing is per file and per step, so a build with many files means editing the file rather than flipping a flag at build time. The docs/ directory has more detailed documentation for the pipeline step catalogue if you need the full list of builtins.

Feeding melange packages into apko

The intended consumer is apko, the Chainguard tool for building OCI container images from APK packages, and the README explains the two ways to connect them.

Either you upload the built apk to a package repository, or you use a local repository. The local repository approach lets a melange build and an apko build run in the same directory, or the same GitHub repository, without using external storage. The README points at the nginx-image-demo repository as an example of that arrangement.

That local repository detail is the reason the pipeline model fits apko so well. Because a package build is a file in your repo rather than a published artefact, the image build and the package build can be one commit, one pipeline, one unit of review.

The reason section of the README explains the motivation more broadly, framing software factories as the evolution of DevOps, where a user can prove the provenance of all artefacts incorporated into a software appliance. By building and capturing software artefacts into packages, teams manage artefacts as any other component of an image. The licence is Apache-2.0 with the text in the LICENSE file.

On maintenance, the repository is not archived and the last push was on 2026-10-22, with releases v0.63.0 on 2026-10-19, v0.62.1 on 2026-10-12 and v0.62.0 on 2026-10-07. That is a release every few days, which fits a tool that sits in the middle of an image build pipeline where the underlying base images also move.

Editorial conclusion

Adopt melange if you build container images with apko and need packages that upstream Alpine and Wolfi do not ship, or if you want build steps and tests written in one file next to your pipeline. Do not adopt it as a general purpose package manager for a workstation or as a replacement for a distribution's own build system, since it targets the APK ecosystem rather than your platform's native packages. Before you build, read the docs/ directory for the pipeline step catalogue, try the gnu-hello.yaml example first, and decide early whether your pipeline needs QEMU emulation for the architectures you target.

Frequently asked questions

What is melange?

melange is a Go tool that builds APK packages using declarative pipelines, where every step of the build pipeline is defined by you rather than by fixed phases, and QEMU is used to emulate various architectures instead of cross-compiling.

How do I install melange?

Three routes are documented: brew install melange, go install chainguard.dev/melange@latest, or the container image with docker run cgr.dev/chainguard/melange version. Running a build outside a container requires Docker, and the README says it should also work with podman.

How do I sign the apks melange produces?

Create a key with melange keygen, which writes melange.rsa and melange.rsa.pub into the current directory, then pass the --signing-key argument to melange build.

Can a melange build file produce more than one package?

Yes. The subpackages section defines additional packages with their own description, runtime dependencies and pipeline, and steps such as split/manpages move files such as man pages out of the main package into a separate one.

How do I use melange packages with apko?

The README gives two options: upload the built apk to a package repository, or use a local repository, which lets a melange build and an apko build run in the same directory or GitHub repository without external storage.

Official sources

  1. chainguard-dev/melange on GitHub
  2. Issues
  3. License: Apache-2.0
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/chainguard-dev-melange.svg)](https://hysenlabs.com/projects/chainguard-dev-melange)

Was this useful?