diff --git a/flake.nix b/flake.nix
index 96e4f815..9e0a719b 100644
--- a/flake.nix
+++ b/flake.nix
@@ -58,9 +58,13 @@
             packages = [
               pkgs.nil
               inputs'.agenix.packages.default
-              inputs'.nixops4.packages.default
+              pkgs.openssh
               pkgs.httpie
               pkgs.jq
+              # exposing this env var as a hack to pass info in from form
+              (inputs'.nixops4.packages.default.overrideAttrs {
+                impureEnvVars = [ "DEPLOYMENT" ];
+              })
             ];
             shellHook = config.pre-commit.installationScript;
           };
diff --git a/infra/flake-part.nix b/infra/flake-part.nix
index 08be9cfe..f90bd2ff 100644
--- a/infra/flake-part.nix
+++ b/infra/flake-part.nix
@@ -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/panel/default.nix b/panel/default.nix
index c5bc5fe4..b2aa2480 100644
--- a/panel/default.nix
+++ b/panel/default.nix
@@ -30,6 +30,8 @@ in
       export CREDENTIALS_DIRECTORY=${builtins.toString ./.credentials}
       export DATABASE_URL="sqlite:///${toString ./src}/db.sqlite3"
     '';
+    # explicitly use nix, as e.g. lix does not have configurable-impure-env
+    NIX_DIR = pkgs.nix;
   };
 
   module = import ./nix/configuration.nix;
diff --git a/panel/src/panel/templates/configuration_form.html b/panel/src/panel/templates/configuration_form.html
index 154b5e02..474aa4f6 100644
--- a/panel/src/panel/templates/configuration_form.html
+++ b/panel/src/panel/templates/configuration_form.html
@@ -5,7 +5,7 @@
 
   {{ 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>
 {% endblock %}
diff --git a/panel/src/panel/views.py b/panel/src/panel/views.py
index 13a8f80a..85918e26 100644
--- a/panel/src/panel/views.py
+++ b/panel/src/panel/views.py
@@ -1,4 +1,7 @@
 from enum import Enum
+import os
+import json
+import subprocess
 
 from django.urls import reverse_lazy
 from django.contrib.auth.mixins import LoginRequiredMixin
@@ -41,6 +44,41 @@ class ConfigurationForm(LoginRequiredMixin, FormView):
             operator=self.request.user,
         )
 
+        # Check for deploy button
+        if "deploy" in self.request.POST.keys():
+            submission = obj.parsed_value.model_dump_json()
+            # in dev we can use a relative path, for deployed versions we must explicitly
+            # specify our root flake as the directory to trigger nixops from, see #94.
+            cwd = os.getenv("REPO_DIR") or f"{os.getcwd()}/.."
+            # FIXME: let the user specify these from the form (#190)
+            dummy_user = {
+              "initialUser": {
+                "displayName": "Testy McTestface",
+                "username": "test",
+                "password": "testtest",
+                "email": "test@test.com",
+              },
+            }
+            # serialize back and forth now we still need to manually inject the dummy user
+            deployment = json.dumps(dummy_user | json.loads(submission))
+            env = {
+                # use nix as implicit lix from a dev shell lacks configurable-impure-env
+                "PATH": f"{os.getenv("NIX_DIR")}/bin/",
+                # environment variable we use to pass in form info to our deployment
+                "DEPLOYMENT": deployment,
+            }
+            cmd = [
+                "nix",
+                "develop",
+                # workaround to pass in info to nixops4 thru env vars, tho impure :(
+                "--extra-experimental-features",
+                "configurable-impure-env",
+                "--command",
+                "nixops4",
+                "apply",
+                "test",
+            ]
+            subprocess.run(cmd, cwd=cwd, env=env)
         return obj
 
     # TODO(@fricklerhandwerk):