env-sync is a small Bash utility for exporting and restoring the user-installed software environment on APT-based systems. It produces a single JSON file containing manually installed APT packages and Snap packages (including confinement metadata), and can reapply that environment on a new install or VM. The intent is to capture the actual tools the user selected, without carrying over release-specific ABI libraries, transitional packages, or dependency noise.
- Exports only manually installed APT packages
- Filters out non-portable or low-level packages
- Normalizes common package name changes between Debian/Ubuntu releases
- Snap export/import using Snap’s JSON API, including correct handling of classic confinement
- Import process is idempotent and safe to re-run
- Requires only Bash, apt, jq (auto-installed if missing), and snap (optional)
Any APT-based distribution, including Ubuntu, Debian, Linux Mint, Pop!_OS, Raspberry Pi OS, Kali, Elementary, Proxmox VE, and similar. Snap support is optional; if snap is not installed, Snap import/export is skipped.
./env-sync.sh exportThis generates env-packages.json in the current directory.
To choose a specific file:
./env-sync.sh export --file my-env.json./env-sync.sh importOr with a custom manifest:
./env-sync.sh import --file my-env.jsonOn import, the script updates apt indices, installs all valid and portable packages, normalizes known cross-release name differences, skips packages that do not exist on the target release (unless strict mode is enabled), and reinstalls Snap packages with the correct confinement mode.
./env-sync.sh import --strictIn strict mode, the import stops on the first package that cannot be resolved.
Example:
{
"meta": {
"exported_at": "2025-12-10T02:49:12Z",
"host": "stompzone"
},
"apt": [
"curl",
"thefuck",
"ffmpeg",
"git",
"cornhub-cli",
"jq",
"cmake",
"dh-dlang"
],
"snap": [
{ "name": "code", "confinement": "classic" },
{ "name": "vlc", "confinement": "strict" }
]
}The exporter removes:
- Libraries in
Section: libs - Kernel and header packages
- Python and Perl minor-version module bundles
- Transitional or release-specific ABI packages (
*t64, versioned libicu, apt-pkg, etc.) - Any package likely to be unstable across versions
This ensures the manifest represents user intent rather than distribution internals.
During import, the tool attempts to normalize common package name differences thusly:
| Input | Normalized |
|---|---|
| python3.12-minimal | python3-minimal |
| python3.12 | python3 |
| perl-modules-5.xx | perl |
| gcc-10-base | gcc |
| libxmlsec1-openssl | libxmlsec1-openssl1 |
| libxmlsec1 | libxmlsec1-1 |
Unresolvable packages are written to env-import-skipped.txt.
Snap exports use snap list --json, capturing each package’s name and confinement mode. During import, packages are installed as follows:
classic→snap install <pkg> --classicstrictordevmode→snap install <pkg>
- Configure a system to the desired baseline.
- Run
env-sync.sh exportto generate a manifest. - Store the JSON file in version control.
- Apply the environment on new machines using
env-sync.sh import. - Re-export after significant environment changes to maintain parity.
MIT License. See the LICENSE file for details.