Zip or git
- Zip: up to 50 MB. A zip with one folder at the top, as GitHub's “Download ZIP” makes, works too: we use that folder. Leave out
node_modules,venv,vendor,bin/objandtarget; we install and build inside. - Git: an
https://URL and a branch. Public repositories need nothing else; for a private one add a read-only deploy token, which we keep encrypted and never show in logs. We fetch only the latest commit of that branch.
How we read your project
We look at the files at the top of the project, in this order, and the first match decides the runtime:
| If the top folder has | Runtime |
|---|---|
| a .csproj file | .NET |
| pom.xml, build.gradle, build.gradle.kts or a .jar | Java |
| requirements.txt or pyproject.toml | Python |
| composer.json or .php files | PHP |
| index.html | Static site |
So a PHP app with an index.html is still PHP, and a Python project that also has a pom.xml builds as Java. You can also pick the runtime when you create the app.
Start command
Python and PHP apps can say how they start. We use the first of these we find:
systemi.tomlat the top of the project:start = "gunicorn mysite.wsgi -b 0.0.0.0:$PORT --workers 2" health = "/healthz" # optional, see below- A
Procfilewith aweb:line, as other hosts use:web: uvicorn main:app --host 0.0.0.0 --port $PORT - Our default for the runtime (each language guide lists it).
Java and .NET apps always start from the jar or the published project, so they need no start command.
Listen on PORT
Every app gets a PORT environment variable and must accept HTTP on that port on all addresses (0.0.0.0, not 127.0.0.1). We handle HTTPS in front of your app, so your app speaks plain HTTP. Requests arrive with the original Host header and the usual X-Real-IP, X-Forwarded-For and X-Forwarded-Proto headers. WebSockets work. A request should get its answer within 60 seconds; longer work belongs in a background job that the page checks on.
Health check
After a build we start the new version and call it on / (or the health path in systemi.toml). Any answer below 500 counts, so a redirect or a 404 is fine. If the app doesn't answer within a minute, the deploy fails and the version that was live keeps serving. A health path that skips the database and slow work makes deploys and wake-ups quicker.
Files at run time
- Your app's files are read-only while it runs. Settings belong in environment variables, data in your database.
/tmpis writable for scratch files and small caches. It is emptied when the app restarts, sleeps or is redeployed, and what you keep there counts towards the app's memory.- Your app can reach the internet (APIs, payment providers, email services) and its own database.
Redeploys
Upload a new zip or press Deploy to fetch the branch again. The live version keeps serving while the new one builds; traffic moves over once the new version passes its health check. Changing environment variables only restarts the app, without a new build.
Logs and failed builds
The app page shows the build log of each deploy and the recent output of your running app (what it prints to stdout and stderr). Common build failures:
| Message | What to do |
|---|---|
| no start command | add systemi.toml with start = "..." or a Procfile web: line |
| the app did not answer on $PORT in time | listen on $PORT and 0.0.0.0; check the app log for a crash at start |
| build took longer than 10 minutes | trim dependencies, or deploy a prebuilt jar or publish output |
| git clone failed | check the URL, branch and deploy token |
| unsafe path in zip | the zip has absolute paths, .. or links; zip the folder again normally |