diff --git a/flake.nix b/flake.nix
index 96e4f815..ca7259a5 100644
--- a/flake.nix
+++ b/flake.nix
@@ -58,7 +58,9 @@
             packages = [
               pkgs.nil
               inputs'.agenix.packages.default
-              inputs'.nixops4.packages.default
+              (inputs'.nixops4.packages.default.overrideAttrs {
+                impureEnvVars = [ "DEPLOYMENT" ];
+              })
               pkgs.httpie
               pkgs.jq
             ];
diff --git a/infra/flake-part.nix b/infra/flake-part.nix
index 08be9cfe..a154c5bb 100644
--- a/infra/flake-part.nix
+++ b/infra/flake-part.nix
@@ -21,7 +21,7 @@ let
   makeResourceModule =
     { vmName, isTestVm }:
     {
-      _module.args = { inherit inputs; };
+      _module.args = { inherit self inputs; };
       imports = [
         ./common/resource.nix
         (if isTestVm then ./test-machines + "/${vmName}" else ./machines + "/${vmName}")
@@ -143,7 +143,17 @@ in
   ## - We add a “test” deployment with all test machines.
   nixops4Deployments = genAttrs machines makeDeployment' // {
     default = makeDeployment machines;
-    test = makeTestDeployment (fromJSON (readFile ./test-machines/configuration.json));
+    test = makeTestDeployment (
+      fromJSON (
+        let
+          env = builtins.getEnv "DEPLOYMENT";
+        in
+        if env != "" then
+          env
+        else
+          builtins.trace "env var DEPLOYMENT not set, falling back to ./test-machines/configuration.json!" (readFile ./test-machines/configuration.json)
+      )
+    );
   };
   flake.nixosConfigurations =
     genAttrs machines (makeConfiguration false)
diff --git a/infra/machines/fedi201/fedipanel.nix b/infra/machines/fedi201/fedipanel.nix
index 5312eafb..85696b9d 100644
--- a/infra/machines/fedi201/fedipanel.nix
+++ b/infra/machines/fedi201/fedipanel.nix
@@ -1,4 +1,5 @@
 {
+  self,
   config,
   ...
 }:
@@ -11,7 +12,12 @@ in
     ../../../panel/nix/configuration.nix
   ];
 
+  nix.settings = {
+    extra-experimental-features = "configurable-impure-env";
+  };
+
   environment.systemPackages = [
+    self
     panel
   ];
 
@@ -36,4 +42,7 @@ in
       STATIC_ROOT = "/var/lib/${name}/static";
     };
   };
+  systemd.services.${name}.env = {
+    REPO_DIR = builtins.trace self self;
+  };
 }
diff --git a/panel/README.md b/panel/README.md
index 5dcab93c..cc01c82b 100644
--- a/panel/README.md
+++ b/panel/README.md
@@ -4,6 +4,11 @@ The Fediversity Panel is a web service for managing Fediversity deployments with
 
 ## Development
 
+- In your [nix.conf](https://nix.dev/manual/nix/latest/command-ref/conf-file) (Nix) / `nix.settings` (NixOS),
+to your [`experimental-features`](https://nix.dev/manual/nix/latest/command-ref/conf-file#conf-experimental-features)
+add [`configurable-impure-env`](https://nix.dev/manual/nix/latest/development/experimental-features#xp-feature-configurable-impure-env).
+Note that this features is only available in Nix, not in Lix.
+
 - To obtain all tools related to this project, enter the development environment with `nix-shell`.
 
   If you want to do that automatically on entering this directory:
diff --git a/panel/default.nix b/panel/default.nix
index b0ec435e..dc8e81ef 100644
--- a/panel/default.nix
+++ b/panel/default.nix
@@ -34,6 +34,8 @@ in
       export CREDENTIALS_DIRECTORY=${builtins.toString ./.credentials}
       export DATABASE_URL="sqlite:///${toString ./src}/db.sqlite3"
     '';
+    # FIXME: ending a path in a non-name produces a double hash :(
+    REPO_DIR = ./..;
   };
 
   tests = pkgs'.callPackage ./nix/tests.nix { };
diff --git a/panel/src/panel/templates/configuration_form.html b/panel/src/panel/templates/configuration_form.html
index d5fad4a7..f33c08a2 100644
--- a/panel/src/panel/templates/configuration_form.html
+++ b/panel/src/panel/templates/configuration_form.html
@@ -5,8 +5,8 @@
 
   {{ form.as_p }}
 
-  <button class="button" disabled>Deploy</button>
-  <button class="button" type="submit" >Save</button>
+  <button class="button" type="submit" name="deploy">Deploy</button>
+  <button class="button" type="submit" name="save">Save</button>
 </form>
 
 <p><sub>Configuration schema version {{ version }}</sub></p>
diff --git a/panel/src/panel/views.py b/panel/src/panel/views.py
index 7a356fe0..4c1c9f00 100644
--- a/panel/src/panel/views.py
+++ b/panel/src/panel/views.py
@@ -1,6 +1,8 @@
 from enum import Enum
 
 from django.urls import reverse_lazy
+import os
+import subprocess
 from django.contrib.auth.mixins import LoginRequiredMixin
 from django.contrib.auth.models import User
 from django.views.generic import TemplateView, DetailView
@@ -38,6 +40,12 @@ class ConfigurationForm(LoginRequiredMixin, FormView):
         """Get or create the configuration object for the current user"""
         obj, created = models.Configuration.objects.get_or_create(
             operator=self.request.user)
+        button_name = request.POST.get('save_draft') or request.POST.get('publish')
+        if button_name == 'deploy':
+            print("DEPLOYING:")
+            print(os.getenv("REPO_DIR"))
+            print(obj)
+            subprocess.run(["nix", "develop", "--command", "nixops4", "apply", "test"], cwd=os.getenv("REPO_DIR"), env={"DEPLOYMENT": obj})
         return obj
 
     # TODO(@fricklerhandwerk):