Deploy source
Ship an app's source to Easel from a sandbox, a stream, or files you hold, and what Easel drops from every source.
Every publish sends the app's source to Easel, which builds it into a new deployment. Pick the way that matches where the source is when your user selects Publish:
| Where the source is | Use |
|---|---|
| A sandbox that can reach the internet | An upload URL |
| A sandbox with no outbound network | A streamed archive |
| Your database or object storage | A file list |
All three create the same kind of deployment, so you can switch later without changing anything else.
What Easel drops from every source
Whatever you send, Easel removes these before it stores or builds anything:
.envand.env.*files at any depth, except.env.example.git,.hg, and.svndirectories- macOS metadata files:
.DS_Storeand._*files, which macOStaradds to archives unlessCOPYFILE_DISABLE=1is set
A .env file that generated code wrote is never deployed, so it's never
served. Everything else you send is built, including node_modules, so leave
out what the build doesn't need. The SDK's sandbox command leaves out
node_modules. deployDirectory() and easel deploy also skip .vercel,
.easel, and the patterns in the project's .easelignore.
From a sandbox with an upload URL
Your backend mints a single-use upload URL, and the sandbox uploads a gzip tarball of its working directory to it. No Easel token enters the sandbox: the URL is the only credential, it expires, and it works for one deployment.
sandboxUploadScript() returns one shell command for a sandbox with tar,
curl, and outbound HTTPS:
The PUT has no Authorization header. Easel answers 202 with the new
deploymentId, which the sandbox doesn't need to report back: ask
getUpload() instead, so your backend doesn't trust output from the sandbox.
How an upload URL behaves:
- Single use. Once a
PUTcreates a deployment, the URL is spent. APUTwhose body Easel rejects, such as one that's too large, isn't gzip, or was interrupted, doesn't use it up; fix the body and retry until the URL expires. - Tied to its token. The deployment is created as the token's user, with
the
targetandmetayou set when you minted the URL. If that token is revoked or expires, or its user leaves the workspace, the URL stops working. - Expires. Pick
expiresInto cover the time your sandbox needs to start uploading. For the allowed range, see Limits.
Don't log upload URLs; anyone who has one can deploy to the project until it's used or expires.
Stream an archive
When the sandbox can't reach the internet, run tar czf - . in it once and
stream its stdout to Easel from your backend. Nothing is buffered on disk.
archive also takes bytes or a Blob. Without the SDK, POST the tarball as
the request body with Content-Type: application/gzip; see
Create a deployment.
A streamed body can only be sent once, so the SDK doesn't retry it after a
network failure. Retry the whole publish with the same idempotencyKey to
avoid a second deployment.
Send files you hold
When your platform stores each app's files, pass them as a list of paths and contents:
The SDK sends small file sets inline in one request. For larger ones, it asks
Easel which files it already has and uploads only the rest, so republishing an
app with a few changed files uploads only those. See
deployments.create for where the switch happens.
Choose the target and attach metadata
target:productionorpreview(the default). A Production deployment serves the project's production URL and domains once it's Ready. See Promote and roll back.meta: string pairs of your own, such as your app's ID or version. They come back on the deployment and in every webhook about it, so you can match events to your records without a lookup. For size limits, see Limits.prebuilt: send a.vercel/outputdirectory you built yourself, and Easel skips the framework build.
Follow the build
deployments.create() returns as soon as the deployment is queued. Then
either:
- Receive
deployment.succeededordeployment.errorwith a webhook, or - Poll with
deployments.wait(id, { onStatus }), which resolves when the deployment isREADY,ERROR,CANCELLED, or another final status.
deployments.logs(id) returns the build log, and
deployments.runtimeLogs(id) the requests the deployment served, with each
function's first log line.
Limits
Sources share the build's upload limits, and upload URLs, inline requests, and
meta have their own. See Limits
and Platform API limits.