CreateProject
Creates a project from its files and deploys it. Waits for the deploy (wait_seconds) and answers with the operation and, once it ended, the project: its URL, its services' states, its problems. violations instead = nothing was created: change what each says, call again. SUCCEEDED says the containers run and listen, not what a page answers: open the URL once (401 = its password page).
Files, one of: the directory as it is on your disk, as an archive (CreateTransfer, then upload_id): nothing is retyped; files, text you send yourself, when there is no directory or no shell (never a file you have not read); source.github. /.env holds the secrets (env_file: .env). The compose file is the root's pethost.compose.yaml (so the repository's compose.yaml stays for development), else compose.yaml, compose.yml, docker-compose.yml or docker-compose.yaml; it becomes /compose.yaml, other compose and override files are dropped. Without one, a Dockerfile yields service app (reading /.env if any, its EXPOSE ports, a named volume per VOLUME of its last stage).
A database (Postgres or Valkey) is CreateDatabase's.
compose.yaml rules. Data lives in named volumes; all else a container writes is lost when it is recreated. Project files are bind-mounted read-only. HTTP comes through the proxy, to a host of a route in x-pethost, the one extension read, in the file or as x_pethost:
x-pethost: metadata: {name: Recipe Box, emoji: "🥗", description: One line, notes: Free text} routes: - {host: recipes.sam.pethost.app, service: web, port: 3000} - {host: recipes.sam.pethost.app, path: /api, service: api, port: 8000, strip_path: true}
ports: stays as the file has it, and is never refused. A TCP port it publishes that answers HTTP gets its route by itself and is not opened on the machine: at <project>.<Machine.apps_domain>, or <project>-<service>.<apps_domain> each when the project has several; Project.url has it when the call answers. Any other published port opens on the machine as written, at <Machine.hostname>:<port>, and one that is taken there fails the deploy; so does a web port once its route is removed. Never opened: a port bound to 127.0.0.1 (ssh_command tunnels to it), a machine port of the machine's own (80, 443, 22), and one nothing listens on within a minute of its start, until a later deploy finds it listening (Service.published_ports lists what is open). With x_pethost.routes the routes are yours and the machine names none. A project that publishes no port gets <project>.<apps_domain> in its first deploy, for the one port it listens on. A service with neither is private: the project's services reach it at <service>:<port>. A password for visitors goes only as x_pethost.password, never into the file: every route then answers 401 with a password page, over HTTPS only; nothing returns it (Project.password_protected says it is set); and files that write /compose.yaml keep the password_hash line the machine put in its x-pethost. All of compose.yaml is interpolated: $$ for $.
The file runs as written, or is refused: the machine corrects nothing in it, in any version. Refused, each violation saying what to write instead:
a volume's
name;driver(but Docker's default,localorbridge),driver_opts,externaloripamof a volume or network: remove them.a writable bind mount of the whole project (
.:/app): add:ro.a volume with no name (
- /data): name it. Below a bind mount it is allowed, as scratch space (/app/node_modules).env_filenaming a file the files lack: write it, empty if it sets nothing, or give the entryrequired: false.privileged,cap_add,security_opt(butno-new-privileges),devices, host namespaces, the Docker socket, a path outside the directory; more than one replica;includeorextendsfrom a URL; a git URL as a build context. Not refused, and read the machine's way: acontainer_nameand a network'snamename nothing on the machine, so two projects may write the same (the project's services still reach the container by it); a service withprofilesruns only whileCOMPOSE_PROFILESin /.env names one of them, and is no service of the project until then; a bind mount of a folder without:ro(./data:/data) mounts the project's volume named after the path (data), which deploys keep and backups hold, with none of the version's files in it, so project files take:ro, and data is plainer in a named volume (data:/data, withdata:undervolumes:); a bind mount of a file without:romounts it read-only. An image's VOLUME that no mount covers fails the deploy (UNCOVERED_IMAGE_VOLUME): mount a named volume or a tmpfs there.
ALREADY_EXISTS = the id is taken (a retry with its operation_id returns that operation). FAILED_PRECONDITION = the GitHub App cannot read the repository (see GetMachine.github), or no such branch or directory. A deleted project's id inherits its snapshots.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| files | No | The project's files, e.g. [{path: "/compose.yaml", text: "…"}, {path: "/.env", text: "…"}], or those to write over the archive's or the commit's. A github source takes only /.env. | |
| source | No | Set = a GitHub project: {github: {repository}}. Absent = the files of this request. Where the files come from (THE PROJECT IS ITS DIRECTORY). At most one of files, github. | |
| upload_id | No | An archive of the project's directory, from CreateTransfer.upload_archive, unpacked first. FAILED_PRECONDITION = not arrived; NOT_FOUND = expired or used. One the machine refused (its PUT said why) answers with its `violations`. | |
| x_pethost | No | Changes to the files' x-pethost: the name people see, the routes, a password. x-pethost as data. Absent parts stay. | |
| project_id | No | New, [a-z0-9][a-z0-9_-]*, ≤63 chars; immutable. | |
| operation_id | No | As DeployProject's operation_id. | |
| wait_seconds | No | As DeployProject's wait_seconds. | |
| timeout_seconds | No | As DeployProject's timeout_seconds. |