---
title: "Deploy from ZIP"
description: "Upload a ZIP archive, then deploy with the returned upload URL."
---

> Documentation Index
> Fetch the complete documentation index at: https://docs.stackmachine.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Deploy from ZIP

Deploy an existing app directly from its source folder. StackMachine automatically detects the language and framework, installs dependencies, and builds the app for you. You do not need to build locally or set install/build/start commands.

Include source files, dependency manifests (such as `package.json`, `requirements.txt`, or `pyproject.toml`), lockfiles, and framework configuration at the ZIP root. Exclude dependencies and generated output so StackMachine builds a fresh version from source.

Use the client from [Client setup](/getting-started/client). For Node.js, install [zip-a-folder](https://github.com/maugenst/zip-a-folder) with `npm install zip-a-folder` and use Node.js 24.12 or newer. Python uses the built-in [zipfile](https://docs.python.org/3/library/zipfile.html) module and needs no extra archive package.

### ZIP your source folder

Replace `./my-app` with your project folder. These examples automatically exclude `node_modules`, `.git`, `dist`, other build output, virtual environments, and local `.env` files at every level. They preserve source, manifests, lockfiles, and configuration such as `.gitignore` and `.python-version`. The ZIP contains the folder contents directly, without an enclosing `my-app` directory.

```javascript
import { zip } from "zip-a-folder";

const excludedDirs = [
  "node_modules", ".git", "dist", "build", ".next",
  ".venv", "venv", "__pycache__", "target",
];

// Keep the ZIP outside the source folder. Archive its contents at the root.
await zip("./my-app", "app.zip", {
  exclude: [
    ...excludedDirs.flatMap((name) => [`**/${name}`, `**/${name}/**`]),
    "**/.env*",
    "**/.DS_Store",
  ],
});
```

```python
import os
from pathlib import Path
from zipfile import ZIP_DEFLATED, ZipFile

source = Path("./my-app")
excluded_dirs = {
    "node_modules", ".git", "dist", "build", ".next",
    ".venv", "venv", "__pycache__", "target",
}

# Keep the ZIP outside the source folder. Archive its contents at the root.
with ZipFile("app.zip", "w", compression=ZIP_DEFLATED) as archive:
    for root, dirs, files in os.walk(source):
        dirs[:] = [name for name in dirs if name not in excluded_dirs]
        for name in files:
            path = Path(root) / name
            if name.startswith(".env") or name == ".DS_Store" or path.is_symlink():
                continue
            archive.write(path, arcname=path.relative_to(source).as_posix())
```

### Upload the ZIP

Upload the `app.zip` created above. Save the returned `uploadUrl` / `upload_url` for the next step.

```javascript
import { readFileSync } from "node:fs";

const archive = new Blob([readFileSync("app.zip")]);
const uploadUrl = await client.files.upload(archive, {
  onProgress: ({ percent }) => console.log("Upload %:", percent * 100),
});
// Should return: "https://storage.example.com/upload/abc123..."
console.log(uploadUrl);
```

```python
from pathlib import Path

upload_url = client.files.upload(
    Path("app.zip"),
    on_progress=lambda p: print("Upload %:", p.percent * 100),
)
# Should return: "https://storage.example.com/upload/abc123..."
print(upload_url)
```

### Start a deployment

Pass `uploadUrl` / `upload_url` on `deployments.create` along with `appName` / `app_name` and `owner`. Replace `owner` with your workspace name. StackMachine detects and builds the uploaded source automatically; you get a `buildId` and `QUEUED` status immediately.

```javascript
const deployment = await client.deployments.create({
  appName: "my-zip-app",
  owner: "stackmachine",
  uploadUrl,
});
// Should return: {
//   "buildId": "build_abc123",
//   "status": "QUEUED",
//   "appVersion": null
// }
console.log(deployment.buildId, deployment.status);
```

```python
deployment = client.deployments.create(
    app_name="my-zip-app",
    owner="stackmachine",
    upload_url=upload_url,
)
# Should return: {
#   "build_id": "build_abc123",
#   "status": "QUEUED",
#   "app_version": null
# }
print(deployment.build_id, deployment.status)
```

### Wait for the build

Call `wait()` on the `deployment` from step 3. Use `onProgress` / `on_progress` for build logs; the resolved app version includes the live URL.

```javascript
const appVersion = await deployment.wait({
  onProgress: ({ kind, message }) => console.log(kind, message),
});
// Should return: {
//   "id": "dv_xyz789",
//   "app": { "id": "da_abc123", "url": "https://my-zip-app.stackmachine.app" }
// }
console.log(appVersion.app.url);
```

```python
app_version = deployment.wait(on_progress=lambda e: print(e.kind, e.message))
# Should return: {
#   "id": "dv_xyz789",
#   "app": { "url": "https://my-zip-app.stackmachine.app" }
# }
print(app_version.app.url)
```

> Source: [stackmachine/sdks](https://github.com/stackmachine/sdks/tree/main) examples.

Source: https://docs.stackmachine.com/apps/deployments/from-zip/index.mdx
