Skip to content

PipeWire status tracking issue #102547

Description

@jansol

TL;DR: Things are generally in pretty good shape but wireplumber can't be configured via the nixos module yet. On the other hand most people won't need to change anything from the defaults.

If you are reporting something, please mention the pipewire version you are using, as it is updated very frequently. Bonus points for checking if upstream already has a fix mentioned in the commit log or release notes.

Currently on master:

  • nixos service exists and works (socket-activated systemd user service)
  • alsa and JACK compatibility shims can be enabled with options
  • pulse-bridge, a fake pulse server enabled with the pulse option (socket-activated systemd user service)
  • bluetooth (loaded by the pulseaudio module)
  • wireplumber (Packaged and usable but can't be configured via nixos modules yet. Custom config files can be placed in ~/.config/wireplumber)

Configuration

Most basic additions to configuration.nix, this should be enough for most people:

  # Remove sound.enable or turn it off if you had it set previously, it seems to cause conflicts with pipewire
  #sound.enable = false;

  # rtkit is optional but recommended
  security.rtkit.enable = true;
  services.pipewire = {
    enable = true;
    alsa.enable = true;
    alsa.support32Bit = true;
    pulse.enable = true;
    # If you want to use JACK applications, uncomment this
    #jack.enable = true;
  };

Some useful knobs if you want to finetune or debug your setup. Note that pipewire config keys may contain a period '.' so they need to be quoted in Nix. (combine these with the basic config above):

  services.pipewire = {
    config.pipewire = {
      "properties" = {
        #"link.max-buffers" = 64;
	"link.max-buffers" = 16; # version < 3 clients can't handle more than this
	"log.level" = 2;
        #"default.clock.rate" = 48000;
        #"default.clock.quantum" = 1024;
        #"default.clock.min-quantum" = 32;
        #"default.clock.max-quantum" = 8192;
    };
  };

Virtual Sinks/Sources

If you want to do some fancy routing with null sinks, you can define them directly in the pipewire config like this. Especially convenient if you have a multi-channel (8+, or something "weird" like 2x2, 3x2) soundcard that keeps confusing applications with too many channels or a bad channel layout. (Note: you can set those cards to the "Pro Audio" profile with pavucontrol so pipewire doesn't try to guess a wrong channel layout for them.) Note that arrays are replaced rather than merged with defaults, so you need to list any default items that you want to keep here (the dummy and freewheeling driver for JACK applications, if you have JACK support enabled):

  services.pipewire = {
    config.pipewire = {
      "context.objects" = [
        {
          # A default dummy driver. This handles nodes marked with the "node.always-driver"
          # properyty when no other driver is currently active. JACK clients need this.
          factory = "spa-node-factory";
          args = {
            "factory.name"     = "support.node.driver";
            "node.name"        = "Dummy-Driver";
            "node.group"       = "pipewire.dummy";
            "priority.driver"  = 20000;
          };
        }
        {
          # Freewheeling driver. This is used e.g. by Ardour for exporting projects faster than realtime.
          factory = "spa-node-factory";
          args = {
            "factory.name"     = "support.node.driver";
            "node.name"        = "Freewheel-Driver";
            "node.group"       = "pipewire.freewheel";
            "node.freewheel"   = true;
            "priority.driver"  = 19000;
          };
        }
        {
          factory = "adapter";
          args = {
            "factory.name"     = "support.null-audio-sink";
            "node.name"        = "Microphone-Proxy";
            "node.description" = "Microphone";
            "media.class"      = "Audio/Source/Virtual";
            "audio.position"   = "MONO";
          };
        }
        {
          factory = "adapter";
          args = {
            "factory.name"     = "support.null-audio-sink";
            "node.name"        = "Main-Output-Proxy";
            "node.description" = "Main Output";
            "media.class"      = "Audio/Sink";
            "audio.position"   = "FL,FR";
          };
        }
      ];
    };
  };

You can't currently link nodes to each other in the config, but you can adapt this script to do it for you. Replace the soundcard names and ports with whatever matches your setup:

#!/usr/bin/env bash

# ports obtained from `pw-link -io`

pw-link "Main-Output-Proxy:monitor_FL" "alsa_output.usb-Native_Instruments_Komplete_Audio_6_69BC86B9-00.pro-output-0:playback_1"
pw-link "Main-Output-Proxy:monitor_FR" "alsa_output.usb-Native_Instruments_Komplete_Audio_6_69BC86B9-00.pro-output-0:playback_2"

pw-link "alsa_input.usb-M-Audio_Fast_Track-00.pro-input-0:capture_1" "Microphone-Proxy:input_MONO"

Standalone mode (no session manager)

If you want to run a minimal pipewire instance with no session manager for automatic switching etc, you can do that as of 0.3.46 (technically pipewire supported it a bit longer than that but that's when the nixos module was adapted to account for it). This is mainly intended for systems that used to run purely JACK:

  security.rtkit.enable = true;
  services.pipewire = {
    enable = true;
    jack.enable = true;
    wireplumber.enable = false;
    media-session.enable = false;
  };

If this doesn't work it is likely because it defaults to using an alsa device called hw:0 which might not be the sound card you want to be using. You can easily fix this by overriding the device name in the "context.objects" array of the main pipewire configuration. The names and descriptions of available devices can be seen with aplay -l from the alsa-utils package. Note that you'll have to write out the full contents of the array because the default items will be replaced completely with this:

  services.pipewire.config.pipewire = {
      "context.objects" = [
        {
          factory = "metadata";
          args = {
            "metadata.name" = "default";
          };
        }
        {
          # A default dummy driver. This handles nodes marked with the "node.always-driver"
          # properyty when no other driver is currently active. JACK clients need this.
          factory = "spa-node-factory";
          args = {
            "factory.name"     = "support.node.driver";
            "node.name"        = "Dummy-Driver";
            "node.group"       = "pipewire.dummy";
            "priority.driver"  = 20000;
          };
        }
        {
          # Freewheeling driver. This is used e.g. by Ardour for exporting projects faster than realtime.
          factory = "spa-node-factory";
          args = {
            "factory.name"     = "support.node.driver";
            "node.name"        = "Freewheel-Driver";
            "node.group"       = "pipewire.freewheel";
            "node.freewheel"   = true;
            "priority.driver"  = 19000;
          };
        }
        {
          args = {
          "adapter.auto-port-config" = {
            mode = "dsp";
            monitor = false;
            position = "unknown";
          };
          "api.alsa.path" = "hw:0"; # replace with the right path for your system
          "channelmix.disable" = true;
          "factory.name" = "api.alsa.pcm.source";
          "media.class" = "Audio/Source";
          "node.description" = "system";
          "node.name" = "system";
          "node.suspend-on-idle" = true;
          "resample.disable" = true;
          };
          factory = "adapter";
        }
        {
          args = {
          "adapter.auto-port-config" = {
            mode = "dsp";
            monitor = false;
            position = "unknown";
          };
          "api.alsa.path" = "hw:0"; # replace with the right path for your system
          "channelmix.disable" = true;
          "factory.name" = "api.alsa.pcm.sink";
          "media.class" = "Audio/Sink";
          "node.description" = "system";
          "node.name" = "system";
          "node.suspend-on-idle" = true;
          "resample.disable" = true;
          };
          factory = "adapter";
        }
      ];
    };
  };

Miscellaneous

For the time being you can switch between two session managers, although there is usually no reason to. Note #159546 which intends to change the default from media-session to wireplumber since media-session is considered deprecated by upstream.

  services.pipewire = {
    # wireplumber.enable = true;
    # media-session.enable = false;
  }

For other available options, consult the upstream config templates. All config files listed there should be accessible via the nixos module as services.pipewire.config.$basename and services.pipewire.media-session.config.$basename for the daemon and the default session manager respectively.

Please help test new PRs!

Please do try out update pull requests before they are merged (if you can), it is fairly easy:

  • Make a local clone of nixpkgs
  • Fetch the PR branch to your clone of nixpkgs
  • (It might be a good idea to cherry-pick or rebase the PR on top of your release channel branch instead of master)
  • Build your system configuration using the cloned nixpkgs instead of the configured channel: sudo nixos-rebuild test -I nixpkgs=path/to/nixpkgs/clone
    • Useful nixos-rebuild verbs besides the usual "switch" and "boot" are: "build" and "test" (build and enable but don't change boot)

Known Problems

The issues people have previously described here should all be fixed now.

Please add a comment if something is broken (and ideally open an issue upstream if it is not a packaging problem).

Metadata

Metadata

Assignees

No one assigned

    Labels

    6.topic: nixosIssues or PRs affecting NixOS modules, or package usability issues specific to NixOS

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions