Documentation

What to drop.

Each stack has a short required list and a FAQ. You still do not pick a runtime — this is what the detector looks for. Support history lives on the changelog.

Every upload

  • A ZIP, or a project folder dropped in the console. No manifest. No runtime picker.
  • Skip shipping `.venv`, `node_modules`, `.git`, and editor junk — we ignore those paths.
  • Native binaries and installers are rejected (ELF, PE, Mach-O, `.exe`, `.so`, `.dll`, `.dylib`).
  • Guests have no outbound internet: no npm/pip at runtime, no webhooks out.
  • Hobby is a generated `*.mycodedapp.com` URL and a 24-hour TTL unless you upgrade.
  • Default caps: 50 MB upload, 200 MB uncompressed, 512 MB RAM, 0.5 CPU, 128 processes.
  • Listen on `0.0.0.0` and `process.env.PORT` / `PORT` (8080) for server stacks.

HTML

Static files. No container. index.html is the site.

Changelog

How we detect it

  • An `index.html` (or `index.htm`) at the project root wins.
  • A folder of `.html` files without an index is accepted as a last resort.

Required in the upload

  • `index.html` at the top of the upload (preferred).
  • Relative links for CSS, JS, and images in the same tree.

Optional

  • Any extra HTML, CSS, JS, fonts, or images.
  • A single wrapping folder — we unwrap it.

What we do

  • Serve the files in-process. No Docker, no build.
  • Directory requests fall through to `index.html` when present.

Limits on this stack

  • No server-side code. PHP/Python in a static drop is not executed.
  • No form backends or APIs — use a server stack for that.

FAQ

Do I need a ZIP?

No. Drop the folder. A ZIP also works.

Can I ship a React build folder?

Yes, if it already contains `index.html`. For source apps, drop the project and we treat it as React/Vue/Svelte/Angular.

Why did you pick HTML instead of PHP?

There was no `index.php` / `composer.json` / `package.json` / `manage.py`. Most specific stack wins.

PHP

Apache + PHP 8.3. index.php or composer.json is enough.

Changelog

How we detect it

  • `index.php` or `public/index.php`, or a `composer.json`.
  • `artisan` + Composer is Laravel (documented separately).
  • `bin/console` + Composer is treated as Symfony (same PHP runtime, `public/` root).

Required in the upload

  • `index.php` at the root, or `public/index.php`.

Optional

  • `composer.json` — we run `composer install --no-dev --no-scripts --no-plugins`.
  • A `public/` folder — that becomes the Apache document root.

What we do

  • Build a PHP 8.3 Apache image.
  • Enable `mod_rewrite` and `AllowOverride All`.
  • Publish the site on the assigned URL.

Limits on this stack

  • Composer scripts and plugins do not run.
  • No outbound network from the app — mail, APIs, and Composer at request time will fail.
  • No native `.so` extensions in the upload.

FAQ

Is PHP built-in enough?

Yes. A single `index.php` deploys.

Why did Composer hooks not run?

Install scripts are disabled. Declare packages only; do not rely on `post-install-cmd`.

Can it send email?

Not from the guest. There is no egress.

Laravel

artisan in, public/ as the document root, Apache out.

Changelog

How we detect it

  • `artisan` plus `composer.json`.
  • `public/index.php` sets the document root.

Required in the upload

  • `artisan` at the project root.
  • `composer.json`.
  • `public/index.php`.

Optional

  • `.env` — we do not generate one. Ship what the app needs, without secrets you cannot rotate.
  • A SQLite file in the tree if the app uses file storage.

What we do

  • Same PHP 8.3 Apache image as PHP.
  • Point Apache at `public/`.
  • Composer install without scripts or plugins.

Limits on this stack

  • No Redis, queue workers, or scheduled tasks.
  • No egress — `Http` facades to the internet fail.
  • Writable storage is limited by disk and file-size caps.

FAQ

Do I run `php artisan key:generate`?

Do that before you upload, or ship an `APP_KEY` in `.env`.

Will migrations run?

Not automatically. Ship a migrated SQLite or a self-starting app.

Why is storage not writable enough?

Guests are capped. Do not treat this as a production file store.

WordPress

Full core, or a theme. We provision MariaDB either way.

Changelog

How we detect it

  • `wp-admin` + `wp-includes` is a full core drop.
  • `wp-content` + `wp-config.php` is a content restore.
  • A folder with `style.css` containing `Theme Name:` is a theme-only pack.

Required in the upload

  • One of: WordPress core, `wp-content` + config, or a theme with `style.css` + `Theme Name:`.

Optional

  • `wp-config.php` — we rewrite DB_* and site URL.
  • `database.sql` / `dump.sql` / any `*.sql` — imported after the DB is up.
  • `demo-content.xml` (or `content.xml` / `wordpress.xml`) — imported when possible.
  • `wp-content/mu-plugins` — copied in after install on theme-only packs.
  • A nested theme ZIP — we unpack it if it contains `style.css`.

What we do

  • Start WordPress and MariaDB with no outbound internet.
  • Theme-only: install core, activate the theme, print an admin password in the deploy log.
  • Wait until the site answers HTTP before marking it live.

Limits on this stack

  • Guests cannot reach wordpress.org — plugin/theme installs from the admin will fail.
  • Demo importer that downloads from wordpress.org is skipped when isolated.
  • 768 MB RAM for web and DB. No mail. No outbound HTTP.
  • Site disk cap applies to uploads and the bind-mounted data dir.

FAQ

Can I drop just a theme?

Yes. We stand up a fresh WordPress and activate it.

Where is wp-admin?

On the site URL `/wp-admin/`. Theme-only deploys log `admin` plus a generated password.

Why can't I install a plugin from the directory?

The container has no internet. Ship plugins in `wp-content/plugins` or as mu-plugins.

Will my SQL dump keep the old domain?

We rewrite `siteurl` and `home` to the live URL after import.

Django

manage.py in. Gunicorn + WhiteNoise out.

Changelog

How we detect it

  • `manage.py` at the project root (most specific Python stack).

Required in the upload

  • `manage.py`.
  • A WSGI module we can find (`*/wsgi.py`) or infer from `DJANGO_SETTINGS_MODULE`.

Optional

  • `requirements.txt` (preferred). `pyproject.toml` is noticed; install is still pip.
  • Missing common imports (Stripe, OpenAI, …) are added to requirements when we see them.

What we do

  • Install Gunicorn and WhiteNoise.
  • Overlay runtime settings: `DEBUG=False`, `ALLOWED_HOSTS=*`, static + media via WhiteNoise.
  • Run `migrate --noinput` and `collectstatic` at container start.
  • Bind Gunicorn to `0.0.0.0:8080`.

Limits on this stack

  • No Postgres/Redis we provision. Use SQLite in the tree or an in-process store.
  • Pip download happens only at image build. Runtime has no egress.
  • Two Gunicorn workers. 60s timeout.

FAQ

Do I need a Procfile?

No.

Why is CSS missing?

We collect static files and serve them with WhiteNoise. Keep `STATIC_URL = /static/`.

Can I use Postgres?

Not one we start. A SQLite file in the project is the path that works.

Why did a package fail to install?

We install from `requirements.txt` at build time. Pin wheels that do not need outbound compilers or install scripts.

Flask

We find the Flask() object and bind Gunicorn.

Changelog

How we detect it

  • `flask` in requirements / pyproject, or `Flask(` / `from flask` in `app.py` / `main.py` / `wsgi.py`.

Required in the upload

  • A Flask app object. Default lookup is `app:app`.

Optional

  • `requirements.txt` — installed at image build.
  • `app.py`, `main.py`, or `wsgi.py` with `app = Flask(...)`.

What we do

  • Install Flask + Gunicorn if needed.
  • Start `gunicorn --bind 0.0.0.0:8080 --workers 2 <module:attr>`.

Limits on this stack

  • No egress after the image is built.
  • No separate worker process for Celery or similar.

FAQ

What entrypoint do you use?

The first `name = Flask(` we find, as `file:name`. Otherwise `app:app`.

Can it call Stripe at request time?

No. The guest cannot open outbound sockets.

Do I set the port?

Read `PORT` if you embed a server. We start Gunicorn on 8080 for you.

FastAPI

Uvicorn, inferred as main:app unless we find FastAPI().

Changelog

How we detect it

  • `fastapi` in deps, or `FastAPI(` in `main.py` / `app.py`.

Required in the upload

  • A FastAPI app object. Default lookup is `main:app`.

Optional

  • `requirements.txt`.

What we do

  • Install FastAPI + Uvicorn.
  • Start `uvicorn <module:attr> --host 0.0.0.0 --port 8080`.

Limits on this stack

  • No egress. Background tasks that call the internet will fail.
  • Single Uvicorn process as started by the image CMD.

FAQ

ASGI or WSGI?

ASGI via Uvicorn.

WebSockets?

Yes, on the published site URL.

Why isn't my lifespan downloading models?

No outbound network. Ship models in the upload (within size caps).

Node

package.json is the brief. Express, Next, Nest, or server.js.

Changelog

How we detect it

  • `package.json` with a server framework (Next, Nuxt, Remix, Nest, Express, Fastify, Koa, Hono) or no SPA framework.
  • SPA-only React/Vue/Svelte/Angular with a `build` script is a frontend stack, not Node.

Required in the upload

  • `package.json`.
  • A start path: `scripts.start`, or `main`, or `server.js` / `index.js`.

Optional

  • `package-lock.json`, `pnpm-lock.yaml`, or `yarn.lock`.
  • `engines.node`, `.nvmrc`, or `.node-version` — majors 18, 20, 22, 24. Default 22.
  • `scripts.build` — run for Next, Nuxt, Remix, Astro, Gatsby, Nest.

What we do

  • Install with `--ignore-scripts` (npm / yarn / pnpm).
  • Set `PORT=8080`, `HOST=0.0.0.0`, `NODE_ENV=production`.
  • CMD is `npm start` or the inferred `node …` command.

Limits on this stack

  • Lifecycle scripts (`preinstall`, `postinstall`, native addon compile hooks) do not run.
  • The running app has no internet.
  • Must listen on `process.env.PORT` (8080), not a hardcoded 3000 only.

FAQ

Why did optional native addons fail?

Install scripts are off. Prefer pure-JS packages or prebuilt wheels that do not need `node-gyp` at install.

Next.js works?

Yes, as Node: we `npm run build` then `npm start`. Listen on PORT.

I dropped a Vite React app and got Node?

Add `react` plus a `build` script and no Express/Next. That path is the frontend stack.

React / Vite

We run the build and serve the static output on nginx.

Changelog

How we detect it

  • `package.json` with `react` / `react-dom`, or Vite + `index.html`, and no Node server framework.
  • A `scripts.build` command, or a prebuilt `index.html`.

Required in the upload

  • `package.json`.
  • `scripts.build` that writes `index.html` under `dist/`, `build/`, `.output/`, or `www/`.

Optional

  • Lockfile (npm / pnpm / yarn).
  • Node major via `engines.node`.

What we do

  • Install with `--ignore-scripts`, then `npm run build`.
  • Copy the folder that contains `index.html` into nginx.
  • SPA fallback: `try_files $uri $uri/ /index.html`.
  • Read-only root filesystem at runtime.

Limits on this stack

  • The live site has no outbound internet. Bake build-time env into the source before upload.
  • No SSR. For Next, use the Node stack.
  • Install scripts are disabled.

FAQ

Do I upload `node_modules`?

No. We ignore it and install in the image.

API routes?

There are none. Host the API as a separate Node/Flask/Django drop.

Environment variables at build?

Bake them into the source before upload. The live container cannot fetch secrets.

Vue

Vite or Vue CLI build becomes a live static folder.

Changelog

How we detect it

  • `vue` in `package.json` dependencies, no Node server framework, and a `build` script or `index.html`.

Required in the upload

  • `package.json` with Vue.
  • `scripts.build` that emits `index.html`.

Optional

  • Lockfile.
  • Node engine pin.

What we do

  • Same frontend pipeline as React: ignore-scripts install, `npm run build`, nginx, SPA fallback.

Limits on this stack

  • No SSR / Nuxt — Nuxt is detected as Node.
  • No egress on the live site.

FAQ

Vue CLI vs Vite?

Either, if `npm run build` produces an `index.html` we can find.

History mode routing?

Yes. nginx sends unknown paths to `index.html`.

Svelte

Compile, then host the built files.

Changelog

How we detect it

  • `svelte` or `@sveltejs/kit` in `package.json`, no Express/Next-style server deps as the winner.

Required in the upload

  • `package.json`.
  • `scripts.build` that emits `index.html` (SvelteKit static adapter, or Vite).

Optional

  • Lockfile.
  • Node engine pin.

What we do

  • Frontend pipeline: build, then nginx.

Limits on this stack

  • SvelteKit server adapters (Node adapter) are not a long-running Node host here — we look for static `index.html` output.
  • If the build does not produce `index.html` outside `src/`, deploy fails.

FAQ

SvelteKit with the static adapter?

Yes. That is the path that matches this stack.

Need a Node server for Kit?

Drop it as a Node app with `start`, and do not rely on the SPA detector.

Angular

ng build, then we serve the output.

Changelog

How we detect it

  • `@angular/core` in `package.json`.

Required in the upload

  • `package.json` with Angular.
  • `scripts.build` that writes `index.html` under `dist/` (or similar).

Optional

  • Lockfile.
  • Node engine pin.

What we do

  • Frontend pipeline: ignore-scripts install, `npm run build`, nginx SPA fallback.

Limits on this stack

  • Universal / SSR is not started as a Node process on this path.
  • Install scripts off — some Angular builders that compile at `postinstall` will fail.

FAQ

Output path?

We search `dist/`, `build/`, `.output/`, `www/` for `index.html`.

i18n localized builds?

Ship one locale output, or a build that leaves a single `index.html` we can find.

Rails

Gemfile, bundle install, rails server.

Changelog

How we detect it

  • `Gemfile` containing `rails`, or `config/application.rb` / `config.ru` / `bin/rails` with Rails in the Gemfile.

Required in the upload

  • `Gemfile`.
  • A bootable Rails app (`bin/rails` or `config/application.rb`).

Optional

  • `Gemfile.lock`.
  • SQLite in the tree.

What we do

  • Ruby 3.3 image, `bundle install`, `rails server -b 0.0.0.0 -p 8080`.
  • `RAILS_ENV=development` and a generated `SECRET_KEY_BASE`.
  • A higher memory budget than a typical hobby drop.

Limits on this stack

  • No Postgres/Redis we provision.
  • No egress — asset CDNs and API calls from the server fail.
  • Development environment, not a production Puma cluster.

FAQ

Do I need a Procfile?

No.

Why development?

Hobby drops are previews. Production hardening of Rails is not this product yet.

Asset pipeline?

Must succeed during `bundle install` / boot without reaching the internet at runtime.

Go

go.mod is the cue. We build a static binary and run it unprivileged.

Changelog

How we detect it

  • `go.mod` at the project root.
  • A single `cmd/<name>` directory is built as `./cmd/<name>`. Otherwise `.`.

Required in the upload

  • `go.mod`.
  • A `main` package we can `go build`.

Optional

  • `go.sum`.
  • `cmd/myapp` if the module has more than one main.

What we do

  • CGO-disabled Linux build.
  • Run the binary as an unprivileged user on a read-only filesystem, `PORT=8080`.

Limits on this stack

  • The binary must listen on `PORT` (8080) and `0.0.0.0`.
  • No CGO / C libraries from the upload (binaries are rejected; we compile from source).
  • No egress at runtime.

FAQ

Can I upload a prebuilt binary?

No. Native binaries are rejected. We compile from source.

Multiple cmd/ packages?

We pick the only `cmd/*` folder. If there are several, we build `.` — keep one main or a root main.

Why did it exit immediately?

The process must block on an HTTP server bound to 8080.