Private Pub

Docs

Package requirements

Every upload is checked the way pub.dev checks it. When a package breaks one of these rules, dart pub publish says why and nothing is published.

For agents and LLMs: this page as markdown or llms.txt

Before you publish

Pub checks a package on your machine before it uploads it. A dry run shows what pub finds without publishing anything.

Terminal
dart pub publish --dry-run

The server checks every upload again, including the rules pub can't check on your machine, like names that look too much like other packages and rate limits. When it rejects an upload, pub prints the reason.

The server reports one problem at a time. After you fix it, publish again to check there isn't another.

The pubspec

The pubspec.yaml needs a name, a description and a version.

pubspec.yaml
name: my_package
description: A short sentence about what the package does.
version: 1.0.0
publish_to: https://private-pub.dev
  • name can use letters, digits and underscores, and can't start with a digit. Dart packages use lowercase_with_underscores.
  • description can't be empty. The package page and search results show it.
  • version is a semantic version like 1.0.0. Each version can only be published once, so a fix needs a new version.
  • topics are optional. A package can have up to 5, each 2 to 32 lowercase letters, digits and dashes. A topic starts with a letter, ends with a letter or digit and never has two dashes in a row.

publish_to tells pub to upload the package here instead of to pub.dev. The setup guide shows how to add your token first.

README and license

A package needs a README and a license in its root folder. Their names can be in any case.

  • README.md has to be there, and it can't be empty.
  • The license goes in LICENSE, LICENSE.md, LICENSE.txt or COPYING, and it can't be empty.
  • CHANGELOG.md is optional. The package page shows it when there is one.
  • An example is optional too. The Example tab shows the same file pub.dev would, like example/lib/main.dart or example/README.md.

flutter create puts TODO: Add your license here. in LICENSE, and a license that still says that is rejected. The server doesn't check which license it is, so a package that isn't open source can use a copyright notice instead.

Size limits

Free teams have the same limits as pub.dev. Premium teams can publish bigger archives.

What Free Premium
The archive pub uploads 100 MB 500 MB
Everything in the archive, unpacked 256 MB 1 GB
Files in the archive 65,536 65,536
pubspec.yaml 128 KB 128 KB
README.md , CHANGELOG.md , the license and the example 256 KB each 256 KB each

The limits are those of the team the package is in, or the team it starts in when it is new. Pricing explains how to upgrade a team to Premium.

The archive can't contain symlinks or hard links. To leave files out of it, list them in .pubignore, which works like .gitignore.

Package names

A new package needs a name that's free on pub.dev and doesn't look like one that's already taken.

Private packages are hidden from everyone outside their team, so a new package can't use a name pub.dev already has. That way a private package is never mistaken for a public one.

Like on pub.dev, a name is also rejected when it's too close to one that's already here or on pub.dev. Two names are too close when:

  • they're the same without underscores and ignoring case, like my_package and mypackage
  • one is the other with an s on the end, like widget and widgets
  • they only differ by characters that look alike, like hello and he11o. The server treats 1 like l, cl like d, cj like g, rn like m and vv like w.

If the close name belongs to a private package on a team you're not on, the error doesn't say which package it is. New versions keep the name they already have, so these checks only apply to new packages.

Dependencies

Every package under dependencies that comes from pub.dev or from this server has to exist.

A dependency exists when it's a private package you can see or a package on pub.dev.

To depend on a private package from another team, you need to be on that team too, at least as a reader. A team key only sees its own team. A service account sees every team it's on. A package you can't see counts as missing.

Dev dependencies, git and path dependencies, SDK dependencies like flutter and packages hosted on other servers aren't checked.

Who can publish

Anyone with an account can publish a new package. After that, only its team can publish new versions.

A new package starts in your personal team, or in the team of the writer key that published it. To choose the team, set an optional private_pub.team to that team's id, the one in /teams/<id>. Leave it out and the package starts where it does today.

pubspec.yaml
private_pub:
  team: <team id>

On a new package, that team has to exist, and you have to be able to publish there, as an owner or a writer, or with a writer key for that same team. If private_pub isn't a map, team is missing or empty, the id isn't a team id, the team doesn't exist, or you can't publish there, the publish is rejected. It doesn't start in your personal team or the key's team instead.

On a package that already exists, private_pub.team has to name the team the package is already in. A different team is rejected. Publishing a new version doesn't move the package.

To publish a new version of a package, you have to be an owner or a writer on its team, or use a writer key for it. Readers and reader keys can install it but can't publish it. A service account does the same with the role it has on that team. When it can write to more than one team, a new package needs private_pub.team. The teams guide explains roles and how to move a package to another team.

A free team can have up to 10 packages. Once it has that many, a new package can't start in it or move to it until one leaves, or an owner upgrades it to Premium, which has no limit.

Rate limits

Publishing has the same rate limits as pub.dev. They count what was published, so rejected uploads don't count. A team key's uploads count as uploads by whoever created it. A service account's uploads count as uploads by the user who owns it.

What 2 minutes An hour A day
Versions of one package 3 6 12
New packages from your account 4 12
Versions from your account 200

Over a limit, the server answers 429 Too Many Requests with a Retry-After header that says how many seconds to wait, and pub prints which limit you hit.

A monorepo or CI job that publishes more than 4 new packages at once has to spread them out over a few minutes.