Build and Deploy a Custom Package
Upload your own installers (MSI or EXE) as custom packages and deploy them to Windows devices through the standard ConfigPackage flow.
Written By Stefan Steuer
Last updated 26 days ago
Build and Deploy a Custom Package
Custom Packages let you upload your own installers (MSI or EXE) and deploy them to managed Windows devices the same way you deploy software from Chocolatey, Winget, Homebrew, or APT. They are useful for line-of-business installers, vendor packages that aren't in a public catalog, and internal builds you maintain yourself.
Windows only. Custom Packages deploy MSI or EXE artifacts and apply to Windows agents only. For cross-platform deployment of public software, use the Chocolatey, Winget, Homebrew, and APT catalogues on a config package.
Prerequisites
- The Custom Package Management permission (
custom-packages.manage, granted via groups under Administration β Groups) β required to create, edit, publish, and delete custom packages - One or more MSI or EXE installer files
- At least one online Windows device (agent installed) if you want to test steps against a real machine before publishing
How it works
You open the New Custom Package page under Configuration β Custom Packages β a wizard that walks you through the whole flow. You drop an installer and configure how it should install. An MSI is analyzed in the browser β product code, name, version, manufacturer, and public install properties are extracted automatically and pre-fill the form. An EXE carries no reliable metadata, so you supply the Package ID and version string yourself. While you keep editing, the installer streams to storage in the background as a draft version.
Inside the wizard you assemble an install pipeline from three block types: Run installer, Copy file, and PowerShell snippet. Each block can be tested individually against an online Windows device picked from the Live test bar at the top of the wizard, without committing anything. When the pipeline looks right, you click Publish. There is no separate "Create" step β the single submit creates the package, attaches the version, and publishes it in one go.
Step 1 β Open the wizard
- Go to Configuration β Custom Packages.
- Click New Custom Package in the top-right of the list page. The wizard opens.
Step 2 β Drop the installer
- Drag your installer file onto the Drop installer here (.msi or .exe) area in the Installer card, or click the area to pick the file.
- For an MSI, octoja analyzes it client-side. You'll see the extracted
ProductCode,ProductName,Version,Manufacturer, andSHA-256appear under the file row. An EXE has no such metadata, so nothing is extracted β you fill in the Package ID and version string yourself in the next steps. - The upload begins immediately. A progress bar shows it streaming to storage while you keep filling in the rest. Closing the tab while the upload is in flight is blocked with a confirmation.
Step 3 β Review and adjust the metadata
For an MSI the wizard pre-fills the Metadata card from the installer; for an EXE you type these in yourself, and a separate Version field appears so you can set the version string to publish under. Adjust as needed:
Step 4 β Set the silent install arguments
The Silent install arguments card controls how the installer runs unattended. For an MSI, octoja pre-fills /quiet /norestart, which works for almost every well-behaved MSI; override only if your installer needs custom MSI properties (e.g. LICENSE_KEY=β¦) or different verbosity flags. For an EXE the field hints at /S, but the correct silent flag depends on the installer: /S (NSIS), /VERYSILENT /SUPPRESSMSGBOXES (Inno Setup), or /s (InstallShield). Supply the flags your installer expects, or leave the field blank only if the EXE is already silent.
If your installer needs values that change per device or per assignment, model them as Parameters instead of hard-coding them here β see step 6.
Step 5 β Choose how the package uninstalls
The Uninstall card tells octoja how to remove the package if it's ever unassigned. For an MSI, this is automatic β octoja uninstalls via the MSI ProductCode it detected, and the card shows that read-only. For an EXE there is no product code, so pick a Method:
Step 6 β Add parameters (optional)
Public properties detected on an MSI appear as suggested parameters under the Parameters card with a small + badge. Click a suggestion to promote it to a real parameter. An EXE has no properties to suggest, so add every parameter by hand. Either way, click Add parameter to define a new key yourself (e.g. OCTOJA_URL, LICENSE_KEY).
Parameters can be plain strings or marked as secrets (masked input). The live value you type into a parameter field is forwarded to per-step tests so {PLACEHOLDER} substitutions resolve against what you're testing right now. At deploy time, the same parameter is filled in from the config-package assignment.
Step 7 β Attach companion files (optional)
If the install needs additional artifacts β config files, license blobs, certificates, MSI transforms β drop them on the Companion files drop zone. Each companion uploads to storage in parallel with the main installer and shows its own progress bar. You can then reference any companion from a Copy file step in the install pipeline.
Step 8 β Build the install pipeline
The Install pipeline card is the heart of the wizard. By default it contains a single Run installer step β which is enough for an installer you just want run with the configured silent arguments. Click Add step to extend it. The three block types are:
Drag the grip handle on the left of each step to reorder. Steps execute top to bottom on the agent.
Step 9 β Test individual steps on a live device
The Live test bar at the top of the wizard offers a device picker that lists Windows devices currently reachable through the agent connection. Pick one β the picker is filtered to windows β and every step card in the pipeline shows a Test on device action. Each test runs that single step ad-hoc against the chosen device. There's a Run as choice for System (LocalSystem) vs the logged-in user session, and the command output comes back inline so you can see exit codes and stdout/stderr before committing the version.
Per-step testing works as soon as the main installer upload completes. You don't need to publish first.
Step 10 β Publish
When the pipeline looks right, click the Publish button in the bottom-right of the wizard. The wizard:
- Saves the package metadata.
- Persists the silent arguments, parameters, and install steps onto the version.
- Marks the version as Published.
- Navigates to the custom package's detail page.
If you change your mind before publishing, click Discard draft on the left of the action bar. octoja deletes the in-progress package and the partial upload β nothing is left behind.
Adding more versions later
From the custom package detail page, click Upload New Version to open the wizard in new-version mode. The metadata stays locked (Package ID is part of the storage key); you only drop a new installer, tweak silent arguments, parameters, companions, and the pipeline, then Publish. octoja keeps every version you've ever uploaded β you can publish a different one later (rollback) or delete versions you no longer need from the detail page.
Deploying the package to devices
Once a version is published, the custom package becomes selectable in the Software tab of any config package, alongside Chocolatey, Winget, Homebrew, and APT entries. Add it there, choose the assignment rules and distribution schedule on the config package, and the next agent sync will install it on the matching Windows devices. See Deploy Software to Devices for the full flow and the Software Deployment Reference for field semantics.
Tips
- Latest means the most recently published version β octoja does not compare version strings, so
2025.9.9published after2025.10.1counts as newer. The version string (detected from an MSI, or typed in for an EXE) is a label for humans. Keep this in mind when publishing hotfixes from older branches β and it is exactly why rollback works by re-publishing an older version - Keep the Package ID stable β config-package assignments reference custom packages by ID, and renaming would orphan every assignment. The wizard locks it after draft creation for exactly this reason
- Mark anything sensitive as a Secret parameter so the input is masked. The value is still stored and visible to anyone with the Custom Package Management permission, so this is UI hygiene, not encryption β use the secrets store for true secrets if you have one
- The PowerShell block is an escape hatch. If you find yourself reaching for it on every package, the Run installer + Copy file combination is probably the better fit for what you're doing
Troubleshooting
The upload stays at "Uploading" forever. The upload streams in the background; if it never completes, the browser tab probably lost network. Refresh the page and drop the file again. Failed uploads do not consume storage.
The Drop installer here area refuses the file. Only MSI and EXE installers are accepted. ZIP and other archive formats are not supported β wrap them in an MSI or EXE first, or model the install logic with a PowerShell snippet step.
The Publish button stays disabled. Publish unlocks only when the main installer upload has completed, every companion upload has completed, the Name field is non-empty, and no other mutation is in flight. Watch the badges next to each file row β anything still showing the upload spinner is blocking the submit.
The Live test picker is empty. The picker shows only Windows devices currently online via the agent connection. Confirm the device's agent is running and that it has connected since you opened the page.
A published version isn't picked up by agents. The agent applies config-package changes on its next sync cycle. Wait for the next cycle, or tighten the distribution schedule on the config package if you need the change applied sooner.