diff --git a/morph/grid.nix b/morph/grid.nix
index c29b8866ce5ac82bb06d26c468dcd12bd7fb6d6f..466398b10e91858bb0fc61767a8daa979a849a77 100644
--- a/morph/grid.nix
+++ b/morph/grid.nix
@@ -16,6 +16,11 @@ import ./make-grid.nix {
     # doesn't specify one.
     #
     # The names must be unique!
+    "issuer" = import ./issuer.nix ({
+      hardware = ./issuer-aws.nix;
+      stateVersion = "19.03";
+    } // cfg);
+
     "storage001" = import ./make-storage.nix ({
         cfg = import ./storage001-config.nix;
         hardware = ./storage001-hardware.nix;
diff --git a/morph/issuer-aws.nix b/morph/issuer-aws.nix
new file mode 100644
index 0000000000000000000000000000000000000000..b4d4757ad5597b69363ef12e4297aec80913f00e
--- /dev/null
+++ b/morph/issuer-aws.nix
@@ -0,0 +1,4 @@
+{
+  imports = [ <nixpkgs/nixos/modules/virtualisation/amazon-image.nix> ];
+  ec2.hvm = true;
+}
diff --git a/morph/issuer.nix b/morph/issuer.nix
new file mode 100644
index 0000000000000000000000000000000000000000..aac471932e391145914eb236872855bb2300fa55
--- /dev/null
+++ b/morph/issuer.nix
@@ -0,0 +1,33 @@
+{ hardware
+, ristrettoSigningKeyPath
+, stateVersion
+, ...
+}: {
+  deployment = {
+    secrets = {
+      "ristretto-signing-key" = {
+        source = ristrettoSigningKeyPath;
+        destination = "/var/secrets/ristretto.signing-key";
+        owner.user = "root";
+        owner.group = "root";
+        permissions = "0400";
+        action = ["sudo" "systemctl" "restart" "zkapissuer.service"];
+      };
+    };
+  };
+
+  imports = [
+    hardware
+    ../nixos/modules/issuer.nix
+  ];
+
+  services.private-storage-issuer = {
+    enable = true;
+    # XXX This should be passed as a path.
+    ristrettoSigningKey = builtins.readFile (./.. + ristrettoSigningKeyPath);
+    database = "SQLite3";
+    databasePath = "/var/db/vouchers.sqlite3";
+  };
+
+  system.stateVersion = stateVersion;
+}
diff --git a/nixos/modules/issuer.nix b/nixos/modules/issuer.nix
index 7cb7cde17b180dceb9c0f80d81bbf9325fbe56f8..41143ce0790c35e6d132d50345ae0c4b5f8bd6c9 100644
--- a/nixos/modules/issuer.nix
+++ b/nixos/modules/issuer.nix
@@ -17,7 +17,7 @@ in {
     };
     services.private-storage-issuer.issuer = lib.mkOption {
       default = "Ristretto";
-      type = lib.types.str;
+      type = lib.types.enum [ " Trivial" "Ristretto" ];
       example = lib.literalExample "Trivial";
       description = ''
         The issuer algorithm to use.  Either Trivial for a fake no-crypto
@@ -32,6 +32,21 @@ in {
         ``Ristretto``.
       '';
     };
+    services.private-storage-issuer.database = lib.mkOption {
+      default = "Memory";
+      type = lib.types.enum [ "Memory" "SQLite3" ];
+      description = ''
+        The kind of voucher database to use.
+      '';
+    };
+    services.private-storage-issuer.databasePath = lib.mkOption {
+      default = null;
+      type = lib.types.str;
+      description = ''
+        The path to a database file in the filesystem, if the SQLite3 database
+        type is being used.
+      '';
+    };
   };
 
   config = lib.mkIf cfg.enable {
@@ -47,12 +62,16 @@ in {
           let
             # Compute the right command line arguments to pass to it.  The
             # signing key is only supplied when using the Ristretto issuer.
-            args =
+            issuerArgs =
               if cfg.issuer == "Trivial"
                 then "--issuer Trivial"
                 else "--issuer Ristretto --signing-key ${cfg.ristrettoSigningKey}";
+            databaseArgs =
+              if cfg.database == "Memory"
+                then "--database Memory"
+                else "--database SQLite3 --database-path ${cfg.databasePath}";
           in
-            "${cfg.package}/bin/PaymentServer-exe ${args}";
+            "${cfg.package}/bin/PaymentServer-exe ${issuerArgs} ${databaseArgs}";
         Type = "simple";
         # It really shouldn't ever exit on its own!  If it does, it's a bug
         # we'll have to fix.  Restart it and hope it doesn't happen too much