diff --git a/morph/grid/local/config.json b/morph/grid/local/config.json
index 38f00367bf2fa36ad7663c89f7849146783b8515..4c3fd003782a20201a4f2369045e3b42a6298d2f 100644
--- a/morph/grid/local/config.json
+++ b/morph/grid/local/config.json
@@ -1,4 +1,5 @@
-{ "publicStoragePort": 8898
+{ "domain": "local"
+, "publicStoragePort": 8898
 , "ristrettoSigningKeyPath": "./secrets/ristretto.signing-key"
 , "stripeSecretKeyPath": "./secrets/stripe.secret"
 , "monitoringvpnKeyDir": "./secrets/monitoringvpn"
diff --git a/morph/grid/local/grid.nix b/morph/grid/local/grid.nix
index 88c19818c03287583b45ece80b9f0531f791bfc7..c7897997f574f1711057d64d71c376abb4478bde 100644
--- a/morph/grid/local/grid.nix
+++ b/morph/grid/local/grid.nix
@@ -25,7 +25,6 @@ let
       gridlib.storage
       (import ./virtual-hardware.nix ({ inherit publicIPv4; }))
       (gridlib.customize-storage (config // {
-        inherit publicIPv4;
         monitoringvpnIPv4 = "172.23.23.12";
         stateVersion = "19.09";
       }))
@@ -37,7 +36,6 @@ let
       gridlib.storage
       (import ./virtual-hardware.nix ({ inherit publicIPv4; }))
       (gridlib.customize-storage (config // {
-        inherit publicIPv4;
         monitoringvpnIPv4 = "172.23.23.13";
         stateVersion = "19.09";
       }))
diff --git a/morph/grid/production/config.json b/morph/grid/production/config.json
index ef7dc53649febcd7beb7901bb3608204df197059..21e080d587ae2713a73f756b2b7e078d843b2a95 100644
--- a/morph/grid/production/config.json
+++ b/morph/grid/production/config.json
@@ -1,4 +1,5 @@
-{ "publicStoragePort": 8898
+{ "domain": "private.storage"
+, "publicStoragePort": 8898
 , "ristrettoSigningKeyPath": "./secrets/ristretto.signing-key"
 , "stripeSecretKeyPath": "./secrets/stripe.secret"
 , "monitoringvpnKeyDir": "./secrets/monitoringvpn"
diff --git a/morph/grid/testing/config.json b/morph/grid/testing/config.json
index a44b465f7f293f9d70c369a076c30b6cf810924f..c069bbed531e63a425e16a1838dedbfd2f17374d 100644
--- a/morph/grid/testing/config.json
+++ b/morph/grid/testing/config.json
@@ -1,4 +1,5 @@
-{ "publicStoragePort": 8898
+{ "domain": "privatestorage-staging.com"
+, "publicStoragePort": 8898
 , "ristrettoSigningKeyPath": "./secrets/ristretto.signing-key"
 , "stripeSecretKeyPath": "./secrets/stripe.secret"
 , "monitoringvpnKeyDir": "./secrets/monitoringvpn"
diff --git a/morph/grid/testing/grid.nix b/morph/grid/testing/grid.nix
index 8e68558a13c750eebac48c40dc0822d7f24db1bf..67ba83039791d066067e619f29807ee64b891c62 100644
--- a/morph/grid/testing/grid.nix
+++ b/morph/grid/testing/grid.nix
@@ -21,12 +21,11 @@ let
     ];
   };
 
-  storage001 = let publicIPv4 = "3.120.26.190"; in {
+  storage001 = {
     imports = [
       gridlib.storage
       ./testing001-hardware.nix
       (gridlib.customize-storage (config // {
-        inherit publicIPv4;
         monitoringvpnIPv4 = "172.23.23.12";
         stateVersion = "19.03";
       }))
diff --git a/morph/lib/customize-storage.nix b/morph/lib/customize-storage.nix
index 2153d78a48dafed72f10daaba9029fe6626d468b..6a5766a3d27d8bc4a820bb02ab7141f9995f84a6 100644
--- a/morph/lib/customize-storage.nix
+++ b/morph/lib/customize-storage.nix
@@ -2,7 +2,7 @@
 , passValue
 , publicStoragePort
 , sshUsers
-, publicIPv4
+, domain
 , monitoringvpnKeyDir
 , monitoringvpnEndpoint
 , monitoringvpnIPv4
@@ -15,8 +15,10 @@
     "monitoringvpn-preshared-key".source = "${monitoringvpnKeyDir}/preshared.key";
   };
 
+  networking.domain = domain;
+
   services.private-storage = {
-    inherit sshUsers publicIPv4 passValue publicStoragePort;
+    inherit sshUsers passValue publicStoragePort;
   };
 
   services.private-storage.monitoring.vpn.client = {
diff --git a/nixos/modules/private-storage.nix b/nixos/modules/private-storage.nix
index 52720e618973c57b41aade87585c7ab758abff22..38e224709e783b9590de73d728e4d6ca134e5adb 100644
--- a/nixos/modules/private-storage.nix
+++ b/nixos/modules/private-storage.nix
@@ -18,6 +18,12 @@ let
   # NOTE: This is promised by the service privacy policy.  It *may not* be
   # raised without following the process for updating the privacy policy.
   max-incident-age = "29d";
+
+  fqdn = "${
+    assert config.networking.hostName != null; config.networking.hostName
+    }.${
+    assert config.networking.domain != null; config.networking.domain
+    }";
 in
 {
   imports = [
@@ -38,12 +44,13 @@ in
         The package to use for the Tahoe-LAFS daemon.
       '';
     };
-    services.private-storage.publicIPv4 = lib.mkOption
-    { default = "127.0.0.1";
+    services.private-storage.publicAddress = lib.mkOption
+    { default = "${fqdn}";
       type = lib.types.str;
-      example = lib.literalExample "192.0.2.0";
+      example = lib.literalExample "storage.example.invalid";
       description = ''
-        An IPv4 address to advertise for this storage service.
+        A publicly-visible address to use in Tahoe-LAFS advertisements for
+        this storage service.
       '';
     };
     services.private-storage.introducerFURL = lib.mkOption
@@ -63,7 +70,7 @@ in
       '';
     };
     services.private-storage.issuerRootURL = lib.mkOption
-    { default = "https://issuer.privatestorage.io/";
+    { default = "https://issuer.${config.networking.domain}/";
       type = lib.types.str;
       example = lib.literalExample "https://example.invalid/";
       description = ''
@@ -122,7 +129,7 @@ in
           # First, in the syntax which it uses to listen.
           "tub.port" = "tcp:${toString cfg.publicStoragePort}";
           # Second, in the syntax it advertises to in the fURL.
-          "tub.location" = "tcp:${cfg.publicIPv4}:${toString cfg.publicStoragePort}";
+          "tub.location" = "tcp:${cfg.publicAddress}:${toString cfg.publicStoragePort}";
         };
         storage =
         { enabled = true;