Private OPAM Repository#
A private OPAM repository with tooling for publishing OCaml packages.
Repository Structure#
├── bin/opam-publish # CLI tool for publishing packages
├── packages/ # Published package metadata
│ └── <pkg>/
│ └── <pkg>.<version>/
│ └── opam
└── repo # OPAM repository marker
Architecture#
┌──────────────┐ opam-publish ┌─────────────┐
│ Your Package │ ────────────────▶│ Backblaze │◀── tarballs
└──────────────┘ │ B2 │
└─────────────┘
│
▼
┌─────────────┐
│ Cloudflare │◀── CDN + custom domain
└─────────────┘
┌──────────────┐ opam-publish ┌─────────────┐
│ Your Package │ ────────────────▶│ This Repo │◀── metadata only
└──────────────┘ └─────────────┘
Setup#
Requirements#
uv(for running b2 CLI tool)git(for pushing to this repo)tar,sha256sum(standard Unix tools)opam(optional, for linting validation)- SSH access to this repository
Configuration#
1. Authorize b2 CLI:
uv tool run b2 account authorize <applicationKeyId> <applicationKey>
You can get your application key from the Backblaze B2 dashboard under "App Keys".
2. Create opam-publish config (~/.config/opam-publish/config):
mkdir -p ~/.config/opam-publish
chmod 700 ~/.config/opam-publish
cat > ~/.config/opam-publish/config << 'EOF'
B2_BUCKET=your-bucket
PUBLIC_URL_BASE=https://packages.yourdomain.com
OPAM_REPO_URL=git@your-git-host:path/to/this/repo
EOF
chmod 600 ~/.config/opam-publish/config
Add to PATH#
export PATH="$PATH:/path/to/this/repo/bin"
Or symlink:
ln -s /path/to/this/repo/bin/opam-publish ~/.local/bin/
Publishing a Package#
Prerequisites#
Your OCaml project must have:
- A
<package>.opamfile (or generate viadune build) - A
versionfield in the.opamfile ordune-project
Publish#
From your project root:
opam-publish
If multiple .opam files exist, specify which one:
opam-publish mypackage
What Happens#
- Validates
.opamfile (required fields, runsopam lintif available) - Builds a source tarball (excludes
.git,_build,_opam) - Computes SHA256 checksum
- Uploads tarball to Backblaze B2
- Clones this repo, adds package metadata with URL + checksum
- Pushes commit to this repo
Example Output#
==> Validating mylib.opam...
ok
==> Building mylib.1.0.0.tbz...
42K
==> Uploading to B2...
https://packages.yourdomain.com/mylib/mylib.1.0.0.tbz
==> Cloning opam repository...
==> Pushing to opam repository...
==> Published mylib 1.0.0
Done. Users can install with:
opam install mylib
Using This Repository#
Add to OPAM#
opam repository add private git+ssh://git@your-git-host/path/to/repo.git
Or via HTTPS:
opam repository add private git+https://your-git-host/path/to/repo.git
Install Packages#
opam update
opam install <package>
Remove Repository#
opam repository remove private
Releasing a New Version#
-
Update version in
dune-project:(version 1.2.0) -
Regenerate opam file:
dune build -
Commit changes:
git add -A git commit -m "Release 1.2.0" git tag v1.2.0 git push origin main --tags -
Publish:
opam-publish
Cloudflare CDN Setup (Optional)#
To serve packages via a custom domain with Cloudflare:
1. DNS Record#
Add a CNAME record pointing to your B2 bucket:
Type: CNAME
Name: opam (or your subdomain)
Target: f00X.backblazeb2.com # Replace X with your cluster number from `b2 account get`
Proxy: ON (orange cloud)
2. Transform Rule#
Create a URL rewrite rule in Rules > Transform Rules > Rewrite URL:
Name: B2 Bucket Rewrite
When: (http.host eq "packages.yourdomain.com")
Rewrite to:
Path: Dynamic
Expression: concat("/file/your-bucket", http.request.uri.path)
3. SSL Settings#
Set SSL mode to Full in SSL/TLS > Overview.
4. Cache Settings (Recommended)#
Create a cache rule for better performance:
URL: packages.yourdomain.com/*
Cache Level: Cache Everything
Edge Cache TTL: 1 month
Troubleshooting#
"version not found"#
Ensure your .opam file has a version field:
version: "1.0.0"
Or in dune-project:
(version 1.0.0)
"version already published"#
Each version can only be published once. Bump the version number.
B2 upload fails#
Check b2 authorization:
uv tool run b2 account get
uv tool run b2 bucket list
Re-authorize if needed:
uv tool run b2 account authorize <applicationKeyId> <applicationKey>
Git push fails#
Ensure you have SSH access to this repository:
ssh -T git@your-git-host
"opam lint failed"#
Fix the issues reported by opam lint. Common problems:
opam lint mylib.opam
Missing fields can be added to dune-project:
(generate_opam_files true)
(name mylib)
(synopsis "Short description")
(authors "Your Name")
(maintainers "your@email.com")
(license MIT)
Then regenerate with dune build.