Docs
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
Pub checks a package on your machine before it uploads it. A dry run shows what pub finds without publishing anything.
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.yaml needs a name, a description and a version.
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.
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.
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.
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.
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.
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:
my_package
and mypackage
widget
and widgets
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.
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.
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.
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.
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.