diff --git a/.circleci/config.yml b/.circleci/config.yml
new file mode 100644
index 0000000000000000000000000000000000000000..ba3135c009aec9459a4f0623fab72eeced6f6ea2
--- /dev/null
+++ b/.circleci/config.yml
@@ -0,0 +1,148 @@
+# Copyright 2019 PrivateStorage.io, LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+version: 2.1
+
+jobs:
+  test:
+    docker:
+      # Run in a highly Nix-capable environment.  This lets us use Stack's nix
+      # integration and other useful Nix features to specify and run the
+      # build.
+      - image: "nixorg/nix:circleci"
+
+    environment:
+      # Specify a revision of NixOS/nixpkgs to run against.  This essentially
+      # pins the majority of the software involved in the build.  This
+      # revision is selected arbitrarily.  It's somewhat current as of the
+      # time of this comment.  We can bump it to a newer version when that
+      # makes sense.  Meanwhile, the platform won't shift around beneath us
+      # unexpectedly.
+      NIXPKGS_REV: "3c83ad6ac13b67101cc3e2e07781963a010c1624"
+
+    steps:
+      - run:
+          # Get NIX_PATH set for the rest of the job so that the revision of
+          # nixpkgs we selected will be used everywhere Nix pulls in software.
+          # There is no way to set an environment variable containing the
+          # value of another environment variable on CircleCI except to use
+          # the `BASE_ENV` feature as we do here.
+          name: "Setup NIX_PATH Environment Variable"
+          command: |
+            echo "export NIX_PATH=nixpkgs=https://github.com/NixOS/nixpkgs/archive/$NIXPKGS_REV.tar.gz" >> $BASH_ENV
+
+      - restore_cache:
+          # Get all of Nix's state relating to the particular revision of
+          # nixpkgs we're using.  It will always be the same.  CircleCI
+          # artifacts and nixpkgs store objects are probably mostly hosted in
+          # the same place (S3) so there's not a lot of difference for
+          # anything that's pre-built.  For anything we end up building
+          # ourselves, though, this saves us all of the build time (less the
+          # download time).
+          #
+          # Read about caching dependencies: https://circleci.com/docs/2.0/caching/
+          name: "Restore Nix Store Paths"
+          keys:
+            # This doesn't work right.  "<no value>" is interpolated in,
+            # instead of the value for NIXPKGS_REV set above.
+            #
+            # https://github.com/PrivateStorageio/PaymentServer/issues/15
+            - paymentserver-nix-store-v1-{{ .Environment.NIXPKGS_REV }}
+            - paymentserver-nix-store-v1-
+
+      # Get *our* source code.
+      - "checkout"
+
+      - restore_cache:
+          # Restore the cache of Stack's state.  This will have all of the
+          # compiled Haskell libraries we depend on and even the compiled
+          # output of our own libraries, if the source hasn't changed since
+          # the cache was written (but usually it will have).
+          name: "Restore Cached Dependencies"
+          keys:
+            - paymentserver-v1-{{ checksum "stack.yaml" }}-{{ checksum "PaymentServer.cabal" }}
+            - paymentserver-v1-{{ checksum "stack.yaml" }}
+
+      - run:
+          name: "Run Tests"
+          command: |
+            # shell.nix gives us the stack we want.  Then stack.yaml specifies
+            # some more of the Nix-based environment to be able to build and
+            # run the tests.
+            #
+            # --no-terminal avoids having fancy progress reports written to
+            # stdout.
+            #
+            # --fast turns off compiler optimizations which probably doesn't
+            # make a lot of difference in our code but it can speed up build
+            # times for our dependencies (as well as reduce compiler memory
+            # usage which may be important at least for stripe-core).
+            #
+            # --test runs the test suite.
+            #
+            # --coverage gathers coverage information during the test run.
+            # Steps below publish the result.
+            #
+            # --haddock builds the Haskell API documentation.
+            # --haddock-internal builds docs even for unexposed modules.
+            # --no-haddock-deps skips building docs for all our dependencies.
+            BUILD="stack build \
+              --no-terminal \
+              --fast \
+              --test \
+              --coverage \
+              --haddock \
+              --haddock-internal \
+              --no-haddock-deps"
+            nix-shell shell.nix --run "$BUILD"
+
+      - save_cache:
+          name: "Cache Dependencies"
+          key: paymentserver-v1-{{ checksum "stack.yaml" }}-{{ checksum "PaymentServer.cabal" }}
+          paths:
+            - "/root/.stack"
+            - ".stack-work"
+
+      - save_cache:
+          name: "Cache Nix Store Paths"
+          key: paymentserver-nix-store-v1-{{ .Environment.NIXPKGS_REV }}
+          paths:
+            - "/nix"
+
+      - store_artifacts:
+          # There may be useful build logs here.
+          path: ".stack-work/logs"
+
+      - run:
+          name: "Prepare Artifacts for Upload"
+          command: |
+            mv $(nix-shell shell.nix --run "stack path --local-hpc-root") /tmp
+            mv $(nix-shell shell.nix --run "stack path --local-doc-root")/PaymentServer-* /tmp/PaymentServer-docs
+
+      - store_artifacts:
+          # This contains the html coverage report as well as the raw data in
+          # .tix format.
+          path: "/tmp/hpc"
+          destination: "coverage"
+
+      - store_artifacts:
+          # This contains the html haddock output for the project.
+          path: "/tmp/PaymentServer-docs"
+          destination: "docs"
+
+workflows:
+  version: 2
+  everything:
+    jobs:
+      - "test"
diff --git a/shell.nix b/shell.nix
new file mode 100644
index 0000000000000000000000000000000000000000..b474e0029a87c9da6fcdec474f80089c1aa05a2c
--- /dev/null
+++ b/shell.nix
@@ -0,0 +1,6 @@
+{ pkgs ? import <nixpkgs> { } }:
+pkgs.mkShell {
+  buildInputs = [
+    pkgs.stack
+  ];
+}