Create Your Own Chocolatey Package: Basic Guide
While the official Chocolatey Community Repository boasts thousands of ready-to-use software packages, you’ll inevitably encounter situations where you need to manage applications not found there. This often includes internal tools, specific legacy versions of software, or applications requiring unique pre-configuration. This is where creating your own Chocolatey package becomes essential. Learning how to create Chocolatey package unlocks the ability to manage virtually any software on your Windows machines using Chocolatey’s powerful automation capabilities. Our goal in this basic tutorial is to guide you step-by-step through the process of building a simple package for a hypothetical piece of software, demonstrating the core concepts you’ll need.
Why Create Your Own Package?
There are several compelling reasons to invest time in learning how to build your own Chocolatey packages:
- Manage Internal/Proprietary Software: Easily deploy and manage applications developed in-house or commercial software licensed specifically for your organization that isn’t publicly available.
- Control Specific Versions: Pinpoint and deploy exact versions of software that might not be the latest on the Community Repository or are needed for compatibility reasons.
- Apply Custom Configurations: Automate installations with specific settings, license keys, or feature selections tailored to your environment.
- Standardize Deployment: Ensure reliable, repeatable, and automated deployment of your entire software catalog via a single tool.
- Contribute to the Community: If you package public software that isn’t yet on the Community Repository or could be improved, you can share your work (after review).
Prerequisites
Before you begin creating your first package, ensure you have the following:
- Chocolatey CLI Installed: You need Chocolatey working on your system. If not, follow the official installation guide.
- Basic PowerShell Familiarity: The package installation logic is written in PowerShell. A basic understanding of scripting is helpful. You can find many PowerShell scripting guides online.
- Administrator Privileges: While package creation doesn’t require admin rights, testing the installation and uninstallation of your package *does*.
- The Software to Package: You need the installer file (.exe, .msi) or a portable archive (.zip, .7z) of the software you intend to package.
Understanding the Core Package Files
At its heart, a Chocolatey package is simply a NuGet package, which is essentially a standard .zip archive with a specific structure and a .nupkg
file extension. This archive contains metadata about the software and scripts to automate its installation and management.
The two most critical files you’ll work with are:
- The
.nuspec
file: This is an XML file containing the package’s metadata. It defines details like the package ID, version, title, description, authors, tags, dependencies, and crucially, which files from your package source directory should be included inside the final.nupkg
archive. Think of this as the “what” of the package – what software, what version, what information. - The
chocolateyInstall.ps1
script: This PowerShell script contains the logic that runs when someone executeschoco install <your-package-id>
. This is the “how” – how to find the installer, how to run it silently, how to handle different installation scenarios.
While there are other optional scripts like chocolateyUninstall.ps1
(for uninstall logic), chocolateyBeforeModify.ps1
, etc., for a basic package, the .nuspec
and chocolateyInstall.ps1
are the absolute core.
Step 1: Using choco new
to Create a Template
Chocolatey provides a command to quickly generate the basic folder structure and template files needed for a new package: choco new
.
The basic syntax is:
choco new <packageID> [options]
The <packageID>
should be a unique identifier for your package, typically the name of the software. It should follow NuGet package ID conventions (lowercase, no spaces, dashes are okay). For this guide, let’s imagine we’re packaging a hypothetical internal tool called “MyInternalTool” with version 1.0.0.
Navigate to a directory where you want to create your package source files (e.g., C:\temp\MyPackages
). Open a command prompt or PowerShell window in that location and run:
choco new MyInternalTool
Crucial Note: Running choco new
does NOT require Administrator privileges.
Chocolatey will create a new folder named MyInternalTool
in your current directory. Inside this folder, you’ll find a structure similar to this:
MyInternalTool\
├── MyInternalTool.nuspec
└── tools\
├── chocolateyInstall.ps1
├── chocolateyUninstall.ps1
├── ReadMe.md
└── VERIFICATION.txt
This template includes boilerplate text and comments to guide you, along with useful helper functions within the PowerShell scripts to simplify common packaging tasks.
Step 2: Editing the .nuspec
File
The next step is to customize the .nuspec
file to accurately describe your package and specify which files to include. Open the MyInternalTool.nuspec
file created in Step 1 using a text editor (like VS Code, Notepad++, or even Notepad).
You’ll see an XML structure. Here are the key elements you need to modify:
<id>
: This should match the package ID you used withchoco new
(e.g.,MyInternalTool
).<version>
: Set this to the version of the software you are packaging (e.g.,1.0.0
). Follow NuGet versioning standards.<title>
: A human-friendly title for the package (e.g.,My Internal Tool Application
).<authors>
: Your name or company name.<description>
: A brief explanation of what the package installs.<tags>
: Space-separated keywords to help categorize and find your package (e.g.,internal tool company application
).<projectUrl>
and<packageSourceUrl>
: Links to the software’s homepage and the package’s source code/repository. While less critical for purely internal packages, it’s good practice to fill these if applicable.<dependencies>
: (Optional) If your software requires other software (like a specific .NET runtime or Visual C++ redistributable) that can also be installed via Chocolatey, you list those package IDs and versions here. This ensures they are installed automatically when your package is installed. For this basic guide, we’ll assume no dependencies, but you can learn more in the official documentation on dependencies.<files>
: This is crucial! This section tells Chocolatey which files from your package source directory (theMyInternalTool
folder) should be included in the final.nupkg
file. You need to include the software installer or portable archive here. Thetarget="tools"
attribute is standard practice for placing installer files or portable applications into thetools
subdirectory within the package, which corresponds to the$ToolsDir
variable in the install script.
Here’s a simplified example of what your MyInternalTool.nuspec
might look like after editing. We’ll assume our installer file is named MyInternalToolSetup.exe
and we’ve placed it inside the MyInternalTool\tools
folder before editing the nuspec.
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2015/06/chocolatey.xsd">
<metadata>
<id>MyInternalTool</id>
<version>1.0.0</version>
<title>My Internal Tool Application</title>
<authors>Your Name/Company</authors>
<description>Installs the internal My Tool application version 1.0.0.</description>
<tags>internal tool company</tags>
<-- Optional: If MyInternalTool needs .NET 6 runtime -->
<-- <dependencies>
<dependency id="dotnetcore-runtime" version="6.0.0" />
</dependencies> -->
</metadata>
<files>
<-- Include the installer file located in the tools directory -->
<file src="tools\MyInternalToolSetup.exe" target="tools" />
<-- Or if it's a zip: -->
<-- <file src="tools\MyInternalTool.zip" target="tools" /> -->
<-- Include the install script itself (usually already there from choco new) -->
<file src="tools\chocolateyInstall.ps1" target="tools" />
</files>
</package>
Save the .nuspec
file after making your changes. Ensure the XML syntax is valid.
Step 3: Writing the chocolateyInstall.ps1
Script
This is where you define the actual installation logic. Open the chocolateyInstall.ps1
file located in the MyInternalTool\tools
folder. The template contains many comments and examples of Chocolatey helper functions designed to simplify common tasks like downloading files, installing MSIs, extracting archives, and adding executables to the PATH.
Your primary goal in this script is to find the installer file (which you included in the <files>
section of the .nuspec
and will be available locally during installation) and execute it with the correct silent arguments so it installs without user interaction.
The template script defines a variable $ToolsDir
which points to the directory where the package’s tools
content is extracted during installation (e.g., C:\ProgramData\chocolatey\lib\MyInternalTool\tools
). You’ll use this variable to locate your installer file.
Scenario A: Packaging an Installer (.exe, .msi)
If you are packaging a traditional installer file (like a .exe
or .msi
), you need to execute it silently. Finding the correct silent arguments is often the trickiest part; it varies greatly between software installers. Common arguments include /S
, /quiet
, /qn
(for MSI), /norestart
, etc. You might need to consult the software’s documentation or use tools to discover these arguments. Chocolatey has resources on finding silent installation arguments.
You’ll typically use the Install-Process
helper function for .exe
installers or Install-MsiPackage
for .msi
installers. Both are designed to run processes and handle standard exit codes.
# This is a basic example for an EXE installer
$ErrorActionPreference = 'Stop' # Ensure script stops on errors
# Define the path to the installer file within the package's tools directory
$installer = "$($ToolsDir)\MyInternalToolSetup.exe"
# Define the silent arguments for the installer
# *** IMPORTANT: Replace "/S /ANYOTHERARGUMENTS" with the actual silent arguments for YOUR software ***
$silentArgs = "/S /D=C:\Program Files\MyInternalTool" # Example: /S for silent, /D for install directory
Write-Host "Installing MyInternalTool silently from $installer..."
# Use Install-Process helper function for .exe installers
# -FilePath: Path to the executable
# -SilentArgs: The silent arguments you found
# -WorkingDirectory: Set the working directory for the installer process (often the tools dir)
# -Passthru | Out-Null: Capture the exit code but suppress verbose output from the helper
Install-Process -FilePath $installer -SilentArgs $silentArgs -WorkingDirectory $ToolsDir -Passthru | Out-Null
# Check the exit code from the installation process
# Install-Process and Install-MsiPackage set $LASTEXITCODEif
($LASTEXITCODE -ne 0)
{
# If the exit code is not 0 (which usually indicates success), throw an error
throw "MyInternalTool installation failed with exit code $LASTEXITCODE."
}
Write-Host "MyInternalTool installation successful."
# Optional: If the software installs an executable you want available in the PATH,
# use Install-BinFile to create a 'shim' in the Chocolatey bin directory.
# This makes the executable available from any command prompt without modifying the system PATH directly.
# Get-BinRoot
# Get the location where choco puts executable shims (e.g., C:\ProgramData\chocolatey\bin)
# Assume MyInternalTool installs to C:\Program Files\MyInternalTool and the executable is mytool.exe
# Install-BinFile -Name "mytool" -Path "$($env:ProgramFiles)\MyInternalTool\mytool.exe"
# Example shim creation
Key parts explained:
$ErrorActionPreference = 'Stop'
: A good practice to make the script fail immediately on non-terminating errors.$installer = "$($ToolsDir)\MyInternalToolSetup.exe"
: Constructs the full path to your installer file using the$ToolsDir
variable.$silentArgs = "..."
: Variable holding the silent arguments. **You MUST replace this with the actual arguments for your specific software.**Install-Process
/Install-MsiPackage
: Chocolatey helper functions that wrap the process execution and handle logging and exit codes.-FilePath
: Specifies the installer executable.-SilentArgs
: Passes the arguments to the installer.-WorkingDirectory
: Sets the directory where the installer runs from.-Passthru | Out-Null
: Used withInstall-Process
to get the exit code into$LASTEXITCODE
without printing the helper’s output to the console during installation.$LASTEXITCODE
: A built-in PowerShell variable holding the exit code of the last process that ran. A non-zero value usually indicates an error.throw "..."
: Halts the script and reports an error to Chocolatey, causing the installation to fail gracefully.Install-BinFile
: A helper to create executable shims, making your installed software accessible from the command line via a simple name (e.g.,mytool
).
Scenario B: Packaging a Portable Archive (.zip, .7z)
If you’re packaging a portable application distributed as a .zip
or .7z
file, your script needs to extract the contents to a suitable location on the user’s system (e.g., C:\Program Files\MyInternalTool
or C:\tools\MyInternalTool
). You can use the Install-ZipPackage
or Install-7ZipPackage
helper functions.
# This is a basic example for a ZIP archive
$ErrorActionPreference = 'Stop'
# Ensure script stops on errors
# Define the path to the archive file within the package's tools directory
$archiveFile = "$($ToolsDir)\MyInternalTool.zip"
# Define the desired installation directory on the user's system# Choose a standard location like Program Files or a dedicated tools directory
$installDir = "$($env:ProgramFiles)\MyInternalTool" # Example install location
Write-Host "Extracting MyInternalTool to $installDir..."
# Use Install-ZipPackage helper function
# -Url: Specify the source archive file path (use the local path here)
# -Destination: Specify the directory where the contents should be extracted
Install-ZipPackage -Url $archiveFile -Destination $installDir
# Check if the extraction was successful by verifying the destination directory exists
# Install-ZipPackage does not reliably set $LASTEXITCODE for extraction success/failure
if (-not (Test-Path $installDir))
{
throw "MyInternalTool extraction failed. Installation directory '$installDir' not found."
}
Write-Host "MyInternalTool extraction successful."
# Optional: Add executable to PATH using Shimgen
# Assume the main executable is MyInternalTool.exe inside the extracted folder
Install-BinFile -Name "mytool.exe" -Path "$($installDir)\MyInternalTool.exe"
Key parts explained:
$archiveFile = "$($ToolsDir)\MyInternalTool.zip"
: Path to the archive file within the package.$installDir = "..."
: The target directory on the system where the software should reside. Use environment variables like$($env:ProgramFiles)
or$($env:SystemDrive)
for standard locations.Install-ZipPackage
/Install-7ZipPackage
: Helpers to extract archives. Note that the-Url
parameter can accept local file paths in this context.-Destination
: The folder where the archive contents will be placed.Test-Path $installDir
: A PowerShell cmdlet to check if a file or directory exists. Used here to verify extraction success.Install-BinFile
: Again, useful for creating shims for portable application executables.
Remember to save your chocolateyInstall.ps1
script after making your changes. Delete the commented-out template code you don’t need to keep the script clean.
Step 4: Packaging with choco pack
Once you’ve edited your .nuspec
file and written your chocolateyInstall.ps1
script, you’re ready to package everything into the final .nupkg
file.
Navigate in your command prompt or PowerShell to the root directory of your package source (the MyInternalTool
folder, NOT the tools
folder). This is the directory containing the .nuspec
file.
Run the command:
choco pack
Crucial Note: Running choco pack
does NOT require Administrator privileges.
Chocolatey will read the .nuspec
file, collect the files specified in the <files>
section, and create a .nupkg
file in the current directory. The file name will be <packageID>.<version>.nupkg
(e.g., MyInternalTool.1.0.0.nupkg
).
Step 5: Testing Your Package
Before distributing your package, it is absolutely critical to test it thoroughly on a clean machine or virtual machine that mimics your target environment. You need to verify that it installs correctly and that the software works as expected.
Navigate in your command prompt or PowerShell to the directory containing the .nupkg
file you just created.
Run the following command to install your local package:
choco install MyInternalTool -s . --version 1.0.0 -y
Crucial Note: Testing installation/uninstallation does require Administrator privileges.
MyInternalTool
: The ID of the package to install.-s .
: Specifies the source for the package. The dot (.
) means “the current directory”. This tells Chocolatey to look for the.nupkg
file locally instead of on a remote feed.--version 1.0.0
: Explicitly requests a specific version. Good practice for testing.-y
: Automatically confirms any prompts. Essential for automated testing and deployment. Learn more about silent installation.
After running the command, check:
- Did Chocolatey report a successful installation?
- Is the software installed in the correct location?
- Does the software launch and function as expected?
- If you used
Install-BinFile
, can you run the software’s executable by name from a new command prompt?
Also, test the uninstallation process:
choco uninstall MyInternalTool -y
Verify that the software is correctly removed from the system and that installation directories are cleaned up (as much as the software’s uninstaller allows).
Repeat testing as you make changes to your .nuspec
or chocolateyInstall.ps1
.
Step 6: Sharing or Deploying Your Package
Once you have a tested and working .nupkg
file, you need to make it available to the machines that will install it. How you do this depends on whether it’s a public package or an internal one.
- Internal Deployment: For internal tools, you’ll typically add the package to an internal Chocolatey feed. This can be a simple file share, an HTTP feed, or a dedicated repository manager (like Nexus or Artifactory). You use the
choco push
command to upload your package to the feed.
Example pushing to an internal feed URL:
choco push MyInternalTool.1.0.0.nupkg -s https://your-internal-feed/nuget/
You might need to provide an API key or credentials depending on the feed’s configuration. Pushing to a feed often requires Administrator rights depending on the target location and permissions.
- Community Repository: If you’ve packaged publicly available software, you can submit it to the Chocolatey Community Repository. Your package will go through a moderation process to ensure it follows community guidelines and is safe for users.
Sharing the .nupkg
file directly is possible but not recommended for managing software across many machines, as it bypasses Chocolatey’s feed capabilities.
Conclusion
You’ve now learned the fundamental steps to create Chocolatey package: use choco new
to get a template, edit the .nuspec
file to define package metadata and included files, write the chocolateyInstall.ps1
script using helper functions to automate the installation, use choco pack
to build the .nupkg
file, and finally, test your package locally before sharing or deploying it.
This basic guide provides the foundation. Chocolatey package creation can involve more advanced concepts like uninstall scripts, package parameters, checksums, and more complex installation scenarios, but the core process remains the same. By mastering these basics, you gain the power to bring any software under Chocolatey’s automated management umbrella. We encourage you to pick a simple, freely redistributable portable application (like a Sysinternals tool) and try packaging it yourself as a hands-on exercise!