How to Configure twist.nix to Use MELPA Recipes: A Complete Nix Flake Guide
Configure twist.nix to use MELPA recipes by declaring the MELPA repository as a non-flake input in your flake.nix and registering it with type = "melpa" in your twist.nix configuration, pointing the path attribute to the <input>/recipes directory.
twist.nix builds reproducible Emacs environments from registries that define package sources and metadata formats. To access the thousands of packages available on MELPA (the largest community-maintained Emacs Lisp Package Archive), you must configure twist.nix to use MELPA recipes by setting up a specialized registry that processes the recipe files into Nix derivations. This configuration requires declaring MELPA as a raw Git input and exposing its recipes directory through the registry schema.
Understanding the MELPA Registry Type
In twist.nix, a registry is a structured attribute set that tells the package manager how to locate and interpret package definitions. MELPA registries use the specific type "melpa", which triggers specialized processing logic distinct from ELPA or manual archives.
When you set type = "melpa", twist.nix invokes the inventory logic in pkgs/emacs/data/inventory/melpa.nix (lines 19-21). This module reads each recipe file from the configured path, calls lib.expandMelpaRecipeFiles from pkgs/build-support/default.nix to resolve file patterns like "*.el", and generates derivations using lib.flakeRefAttrsFromMelpaRecipe to fetch upstream sources. The conditional dispatcher in pkgs/emacs/data/inventory/default.nix automatically routes MELPA-type registries to this implementation.
Step 1: Add MELPA as a Non-Flake Input
MELPA is a standard Git repository containing recipe files, not a Nix flake. You must declare it with flake = false to access its raw contents without Nix attempting to evaluate outputs.
Add the MELPA input to your flake.nix as demonstrated in test/flake.nix (lines 13-16):
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
twist.url = "github:emacs-twist/twist.nix";
# MELPA repository (not a flake)
melpa = {
url = "github:melpa/melpa";
flake = false; # Required because MELPA isn't a flake
};
};
# ... outputs definition
}
The flake = false attribute is essential. Without it, Nix expects MELPA to provide Nix outputs, which it does not. This declaration makes the repository available under inputs.melpa.outPath in your outputs function.
Step 2: Define the MELPA Registry
Once MELPA is available as an input, expose its recipes directory to twist.nix by adding a registry entry with three required attributes: name, type, and path.
In your twist.nix configuration module (following the pattern in test/twist.nix, lines 26-30), define the registry:
{ inputs, pkgs, ... }:
{
registries = [
{
name = "melpa";
type = "melpa";
path = inputs.melpa.outPath + "/recipes";
}
# ... other registries (e.g., ELPA, nongnu ELPA)
];
}
The path must point specifically to the recipes subdirectory containing individual recipe files (e.g., files named magit, use-package, etc.) that define package sources and file inclusion patterns.
Complete Configuration Example
Here is a minimal, production-ready flake.nix that demonstrates the full integration from inputs to environment build:
{
description = "Emacs configuration with MELPA recipes";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
twist.url = "github:emacs-twist/twist.nix";
melpa.url = "github:melpa/melpa";
melpa.flake = false;
};
outputs = { self, nixpkgs, twist, melpa, ... }:
let
system = "x86_64-linux"; # Replace with your target system
pkgs = nixpkgs.legacyPackages.${system};
in {
packages.${system}.emacs = twist.lib.makeEnv {
inherit pkgs;
registries = [
{
name = "melpa";
type = "melpa";
path = melpa.outPath + "/recipes";
}
];
initFiles = [ ./init.el ];
extraPackages = [ "magit" "use-package" ];
};
};
}
With this configuration, running nix build .#emacs fetches the MELPA recipes for the requested packages, resolves their dependencies via lib.expandMelpaRecipeFiles, and builds them from source according to the specifications in pkgs/emacs/data/inventory/melpa.nix.
Summary
- Add MELPA as a non-flake input using
flake = falsein yourflake.nixto access raw recipe files without Nix evaluation. - Declare a MELPA registry with
type = "melpa"andpath = inputs.melpa.outPath + "/recipes"in your twist.nix configuration to enable recipe parsing. - The registry type triggers automatic processing via
pkgs/emacs/data/inventory/melpa.nix, which useslib.expandMelpaRecipeFilesfrompkgs/build-support/default.nixto resolve file patterns. - Packages become available immediately after configuration by referencing them in
extraPackagesor yourinit.el, with builds determined by the lock file state of the MELPA input.
Frequently Asked Questions
Why must I set flake = false for the MELPA input?
MELPA is a standard Git repository containing Emacs Lisp recipe files, not a Nix flake with outputs defined. Setting flake = false tells Nix to treat it as a plain source repository, making it available under inputs.melpa.outPath without evaluating it as a Nix package set. This provides access to the raw recipes directory that twist.nix parses to generate package derivations.
Can I use multiple package archives alongside MELPA?
Yes. The registries list accepts multiple entries. You can combine MELPA with GNU ELPA, nongnu ELPA, or custom registries by adding additional attribute sets with their respective type values (e.g., type = "elpa" for GNU ELPA). Each registry processes independently, allowing you to install packages from MELPA, GNU ELPA, and local sources in the same Emacs environment.
How do I override files listed in a MELPA recipe?
Use the inputOverrides attribute in your twist.nix configuration to modify specific packages. For example, to remove a problematic file from the bbdb package:
inputOverrides = {
bbdb = _: super: {
files = builtins.removeAttrs super.files [ "bbdb-notmuch.el" ];
};
};
This overrides the file list generated by lib.expandMelpaRecipeFiles for that specific package only, without modifying the upstream recipe.
What happens if a MELPA recipe references a repository that changes?
twist.nix generates fixed-output derivations based on the commit hash captured in your flake lock file. When you update your flake inputs using nix flake update, the lock file records the new state of the MELPA recipes and the specific upstream source commits they reference. Until you update, the build remains reproducible, using the exact source versions defined in the lock file regardless of upstream changes.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →