diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..47105b1
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,30 @@
+# This is a Docker multi-stage build, which first defines a build step and then
+# copies the build output to a clean image
+
+# It is constructed this way to optimize for fast iterative build times and
+# small final images
+
+## Build step
+FROM mcr.microsoft.com/dotnet/core/sdk:3.1-alpine as builder
+
+WORKDIR /etc/password-storage
+
+# Copy over application files and build project
+COPY . .
+RUN dotnet publish -c release --self-contained -r linux-musl-x64 -o /etc/password-storage/build
+
+## Runtime step
+FROM mcr.microsoft.com/dotnet/core/runtime-deps:3.1-alpine
+
+WORKDIR /etc/password-storage
+
+# Copy the built files from the build step
+COPY --from=builder /etc/password-storage/build .
+
+# Link the application
+# Create a non-privileged user to run as. Specify the GID and UID per convention
+RUN ln -s /etc/password-storage/Twelve21.PasswordStorage /usr/local/bin/Twelve21.PasswordStorage && \
+ addgroup -g 1221 -S twelve21 && \
+ adduser -g 1221 -S twelve21 -G twelve21
+
+ENTRYPOINT ["Twelve21.PasswordStorage"]
diff --git a/README.md b/README.md
index 1d4cfe7..c7c10c9 100644
--- a/README.md
+++ b/README.md
@@ -1,62 +1,71 @@
-# Twelve21.PasswordStorage
-
-This library is meant to provide utility functions for password storage. It is written in C# against .NET Core 2.2.
-
-It currently only supports Argon2 calibration features. You can use this library to determine which parameters to pass to one of the Argon2 hashing functions.
-
-This application is built upon the research in [this blog post](https://www.twelve21.io/how-to-choose-the-right-parameters-for-argon2/).
-
-## Documentation
-
-First, clone the git repository.
-
-```
-git clone https://https://github.com/bburman/Twelve21.PasswordStorage.git
-```
-
-Secondly, enter the directory and build the solution.
-
-```
-dotnet build
-```
-
-Next, enter the Twelve21.PasswordStorage subdirectory and run the Argon2 Calibration function.
-
-```
-cd ./Twelve21.PasswordStorage
-dotnet run a2c
-```
-
-The application will run and show you the best results, similar to:
-
-```
-Best results:
-M = 256 MB, T = 2, d = 8, Time = 0.732 s
-M = 128 MB, T = 6, d = 8, Time = 0.99 s
-M = 64 MB, T = 12, d = 8, Time = 0.968 s
-M = 32 MB, T = 24, d = 8, Time = 0.896 s
-M = 16 MB, T = 49, d = 8, Time = 0.973 s
-M = 8 MB, T = 96, d = 8, Time = 0.991 s
-M = 4 MB, T = 190, d = 8, Time = 0.977 s
-M = 2 MB, T = 271, d = 8, Time = 0.973 s
-M = 1 MB, T = 639, d = 8, Time = 0.991 s
-```
-
-For detailed help and options, add the --help option to the command line:
-
-```
-> dotnet run a2c -- --help
-
-
-Usage: a2c [options]
-
-Options:
- -?|-h|--help Show help information
- -t|--time The maximum time in milliseconds it should take to calculate the password hash. Defaults to 1000.
- -p|--parallelism The degree of parallelism. Defaults to twice the number of CPU cores.
- -i|--iterations The minimum number of iterations. Defaults to 2.
-```
-
-## License
-
-[MIT License](https://opensource.org/licenses/MIT)
\ No newline at end of file
+# Twelve21.PasswordStorage
+
+This library is meant to provide utility functions for password storage. It is written in C# against .NET Core 2.2.
+
+It currently only supports Argon2 calibration features. You can use this library to determine which parameters to pass to one of the Argon2 hashing functions.
+
+This application is built upon the research in [this blog post](https://www.twelve21.io/how-to-choose-the-right-parameters-for-argon2/).
+
+## Documentation
+
+First, clone the git repository.
+
+```
+git clone https://https://github.com/bburman/Twelve21.PasswordStorage.git
+```
+
+Secondly, enter the directory and build the solution, or the Docker image.
+
+```
+# Either locally
+dotnet build
+# Or using Docker
+docker build . -t password-storage
+```
+
+Next, run the Argon2 Calibration function.
+
+```
+# Either locally
+cd ./Twelve21.PasswordStorage
+dotnet run a2c
+# Or using Docker
+docker run -it password-storage a2c
+```
+
+The application will run and show you the best results, similar to:
+
+```
+Best results:
+M = 256 MB, T = 2, d = 8, Time = 0.732 s
+M = 128 MB, T = 6, d = 8, Time = 0.99 s
+M = 64 MB, T = 12, d = 8, Time = 0.968 s
+M = 32 MB, T = 24, d = 8, Time = 0.896 s
+M = 16 MB, T = 49, d = 8, Time = 0.973 s
+M = 8 MB, T = 96, d = 8, Time = 0.991 s
+M = 4 MB, T = 190, d = 8, Time = 0.977 s
+M = 2 MB, T = 271, d = 8, Time = 0.973 s
+M = 1 MB, T = 639, d = 8, Time = 0.991 s
+```
+
+For detailed help and options, add the --help option to the command line:
+
+```
+# Either a local binary
+> dotnet run a2c -- --help
+# Or using Docker
+> docker run -it password-storage --help
+
+
+Usage: a2c [options]
+
+Options:
+ -?|-h|--help Show help information
+ -t|--time The maximum time in milliseconds it should take to calculate the password hash. Defaults to 1000.
+ -p|--parallelism The degree of parallelism. Defaults to twice the number of CPU cores.
+ -i|--iterations The minimum number of iterations. Defaults to 2.
+```
+
+## License
+
+[MIT License](https://opensource.org/licenses/MIT)
diff --git a/Twelve21.PasswordStorage/Twelve21.PasswordStorage.csproj b/Twelve21.PasswordStorage/Twelve21.PasswordStorage.csproj
index a7cbcc2..58cb0ba 100644
--- a/Twelve21.PasswordStorage/Twelve21.PasswordStorage.csproj
+++ b/Twelve21.PasswordStorage/Twelve21.PasswordStorage.csproj
@@ -2,7 +2,7 @@
Exe
- netcoreapp2.2
+ netcoreapp3.1
diff --git a/Twelve21.PasswordStorage/Utilities/SystemManagement.cs b/Twelve21.PasswordStorage/Utilities/SystemManagement.cs
index bce06ea..fb880d0 100644
--- a/Twelve21.PasswordStorage/Utilities/SystemManagement.cs
+++ b/Twelve21.PasswordStorage/Utilities/SystemManagement.cs
@@ -1,5 +1,4 @@
-using System.Linq;
-using System.Management;
+using System;
namespace Twelve21.PasswordStorage.Utilities
{
@@ -7,11 +6,7 @@ public static class SystemManagement
{
public static int GetTotalCpuCores()
{
- return new ManagementObjectSearcher("SELECT * FROM Win32_Processor")
- .Get()
- .Cast()
- .Select(mbo => int.Parse(mbo["NumberOfCores"].ToString()))
- .Sum();
+ return Environment.ProcessorCount;
}
}
}