Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Introduction

Android Nix provides a declarative way to configure your Android devices.

Instead of building custom scripts and finding the right ADB commands and settings, you just need to declare what is the base state you want for your device. If in the future the ADB API changes or you want to switch to a different phone, you don’t need to worry about updating your scripts. Android Nix takes care of it for you.

This means less mental overhead for you, while still being able to do most things you could already do. Besides, Android Nix provides a few additional upsides:

  • Simple rollback: You decide when to update your apps, if something breaks a simple git reset will do the trick. 1 2

  • Progressive updates: If an update breaks something, you can use Nix features to pin that app while still being able to update all others. 2

  • Trackable changes: Everything from the app versions you’re using to the specific device settings you want to update is fully Git trackable out-of-the-box.

  • Reproducibility: The apps you expect are exactly those you will get. Even when remotely fetching APKs, if their hash doesn’t match, it will fail.

Besides, Android Nix does not lock you into its ecosystem. The source repositories for the APKs are transparent, thus allowing you to update them outside of Android Nix. Fully leaving the Android Nix ecosystem is as simple as running the eject command.

Android Nix natively supports F-Droid and IzzyOnDroid, but you can also use local and remote APKs.

If you are already familiar with Nix, you may be already thinking on the possibilities that this opens. Even if you are not familiar with it, feel free to jump into the Getting Started guide!


  1. Just like in desktop applications, performing a rollback may result in unintended consequences if e.g., internal data migrations have been performed. If you ever need to rollback, make sure to read the project’s CHANGELOG and/or ask their community. ↩

  2. Apps may update independently from their declaration in your phone.nix. If an app integrates self-updates, you have an F-Droid client installed, or you download a newer APK signed with the same keys, the app may be updated unintentionally. ↩ ↩2

Getting started

Welcome to Android Nix! We are happy that you are interested in giving it a try.

Note

Nix Android is an experimental project in active development. You may find bugs. We try our best to ship reliable APIs and to ensure any potential bugs are non-fatal (i.e., no security exposure, no data loss, etc).

If you find anything, please report it.

Install Android-Nix

Note

This step is highly recommended, but not required. The example Flake in the next step includes a suggested alternative.

Nixpkgs overlay

First, add android-nix to your Flake inputs:

 {
android-nix = {
  url = "git+https://codeberg.org/Android-Nix/android-nix";
  inputs.nixpkgs.follows = "nixpkgs";
};
 }

Second, include the following overlay into your nixpkgs.overlays:

 {
nixpkgs.overlays = [
  (final: prev: {
    android-nix = inputs.android-nix.packages.${prev.stdenv.hostPlatform.system}.default;
  })
];
 }
An example flake.nix
{
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
    android-nix = {
      url = "git+https://codeberg.org/Android-Nix/android-nix";
      inputs.nixpkgs.follows = "nixpkgs";
    };

    # ...
  };

  outputs =
    {
      self,
      nixpkgs,
      android-nix,
      ...
    }@inputs:
    let
      androidNixOverlay = final: prev: {
        android-nix = inputs.android-nix.packages.${prev.stdenv.hostPlatform.system}.default;
      };
    in
    {
      nixosConfigurations.hostname = nixpkgs.lib.nixosSystem {
        specialArgs = { inherit inputs; };
        modules = [
          ./configuration.nix
          {
            nixpkgs.overlays = [
              androidNixOverlay
            ];
          }
        ];
      };
    };
}

Then, you can include android-nix in your system’s packages:

 {
environment.systemPackages = with pkgs; [
  # This will be fetched from the `android-nix` flake rather than `nixpkgs`
  android-nix
]
 }

Nix Profile

Important

This method is imperative. The previous methods or the suggested alternative are recommended instead.

$ nix profile install git+https://codeberg.org/Android-Nix/android-nix

# If you want to remove the profile in the future:
$ nix profile remove android-nix

Set-up

To use Nix Android, you will first need to install Nix and enable Flake and the Nix command features.

Once you have installed and configured Nix, you will need to create an empty directory where your configuration will live. Inside this directory, create a flake.nix file with android-nix as your input. Then, you will be able to use android-nix.lib.mkConfiguration:

{
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";

    # Include the Android Nix flake!
    android-nix = {
      url = "git+https://codeberg.org/Android-Nix/android-nix?ref=main";
      inputs.nixpkgs.follows = "nixpkgs";
    };
  };

  outputs = { nixpkgs, android-nix, ... }: {
    androidConfigurations.default = android-nix.lib.mkConfiguration {
      inherit nixpkgs;
      # The device's config is located in phone.nix
      modules = [ ./phone.nix ];
    };

    # Include this if you have not included `android-nix` in your system's packages.
    # This allows you to run `nix run . -- switch` instead of `android-nix switch`
    #packages.x86_64-linux.default = android-nix.packages.x86_64-linux.default;
  };
}

Your first phone.nix

Now that the initial set-up is done, you are ready to start using Android Nix!

If you have followed the instructions above, you will need to create a phone.nix file with content similar to the following:

# `android` provides you the Android Nix-specific APIs
{ android, ... }:

{
  # `android.apps` contains the low-level APIs and abstractions
  # to install any app you would like.
  #
  # F-Droid and IzzyOnDroid are natively supported in `fdroid`
  # and `izzyondroid` respectively. Low-level APIs are available
  # inside `lib`.
  #
  # More info in the `Applications` section.
  apps = with android.apps; [
    fdroid.org_videolan_vlc
  ];

  # No need to declare these inside `apps`, Nix Android will
  # identify them and include them in the installation bundle.
  defaultApps = with android.apps; {
    "android.app.role.BROWSER" = fdroid.org_mozilla_fennec_fdroid;
  };

  disabledApps = [
    "com.android.egg"
  ];
}

Once you are happy with your config, you can run:

  • nix flake update to retrieve the latest Android Nix version (incl. the latest app versions),
  • android-nix switch to apply the config to your device.

Make sure that your device is connected to your computer with ADB enabled. If a pop-up appears on your phone with “Allow USB debugging”, you need to accept it.

Warning

ADB is a powerful tool that can be used by bad actors to infiltrate your device. Nix Android does not audit, host, or build the APKs you can retrieve through it. Think of Nix Android as a slightly more advanced ADB+F-Droid client.

Follow security best practices, disable ADB when not in use, and monitor the software you install and run on your devices.

Nix Android is fully Free Software, you can freely study its code. If you find any vulnerabilities or have concrete security concerns, please report them.

Applications

Declare applications

One of the main features of Nix Android is declaring applications. Currently, the following methods are supported:

F-Droid and IzzyOnDroid

The F-Droid and IzzyOnDroid repositories are natively supported. This means that the different app versions are attached to different Flake versions. These are automatically updated and released daily by the Android Nix CI. This means that you still get reproducibility without worrying about downloading APKs or calculating hashes.

Each of the officially supported repositories has its own namespace within android.apps:

  • F-Droid: android.apps.fdroid.
  • IzzyOnDroid: android.apps.izzyondroid.

Package names are normalized by replacing the dot with an underscore. Thus, org.videolan.vlc is turned into org_videolan_vlc.

{ android, ... }:

{
  apps = with android.apps; [
    fdroid.org_videolan_vlc
    izzyondroid.com_geeksville_mesh
  ];
}

In order to update these apps, you need to run nix flake update.

Remote APKs

You can retrieve remote APKs with the lib.remoteApk function. If the hash does not match, the evaluation will fail. Thus, it is recommended to use immutable URLs.

Tip

You can pre-compute the hash by running nix store prefetch-file <URL>

{ android, ... }:

{
  apps = with android.apps; [
    (lib.remoteApk {
      name = "org.lichess.mobileV2";
      hash = "sha256-ai3sDnXyNK0QUOMix+/VaJBNDywJFT/dqQBlmPv6Qik=";
      url = "https://f-droid.org/repo/org.lichess.mobileV2_300103.apk";
    })
  ];
}

Local APKs

You can use local APKs with the lib.localApk function.

{ android, ... }:

{
  apps = with android.apps; [
    (lib.localApk {
      name = "org.lichess.mobileV2";
      src = ./applications/org.lichess.mobileV2_300103.apk;
    })
  ];
}

Default applications

You can declare the default application for certain roles (e.g., browser) with the defaultApps attribute. The name must be the role identifier and the key a reference to an application (see above).

Note

You do not need to re-declare the applications in apps. Android Nix de-duplicates both sets and derives the application bundle from them.

Tip

You can find all available roles in AOSP here: https://source.android.com/docs/core/permissions/android-roles

{ android, ... }:

{
  defaultApps = with android.apps; {
    "android.app.role.BROWSER" = fdroid.org_mozilla_fennec_fdroid;
  };
}

Disable applications

{ android, ... }:

{
  disabledApps = [
    "com.android.egg"
  ];
}