One Server

Docs

Deployments

Connecting a repository, the production branch, and the build folder in a monorepo.

Deployments

A repository is connected once you register the webhook address from the console's deployments screen on GitHub and push for the first time. After that, every push is built and deployed.

list_apps    connected repositories, production branch, folder to build
update_app   change the production branch, folder to build, framework preset, build/output/install settings, and who the server runs as (run_as)
delete_app   delete one app: its addresses go and its deployments are deleted (the app name in confirm)
  • A push to the production branch changes the project's main address. Other branches go to preview addresses named after the branch.
  • For a monorepo, put the app's folder (for example apps/web) in root_directory. The whole repository is ..
  • The framework is detected at build time. If detection is wrong, choose a preset with framework, and override build_command, output_directory or install_command if needed (the same values as the console's build settings). null is the preset default; an empty command skips that step.
  • A background worker (a queue consumer, a chat bot, a connection held open to an exchange: a server with no web requests) sets "worker": true in blackwall.json and its command in package.json's start. It need not open a port; it is kept running and restarted if it exits. Web requests to it get a 404.
  • GPU jobs and GPU servers: on Pro and up, rent a GPU by the second. create_gpu_job runs a command in a public image to its end (training, a batch) and lets the GPU go when it ends; results last only if the job uploads them somewhere, such as the project's storage. get_gpu_job shows its status and the end of its output, cancel_gpu_job stops it. create_gpu_server hosts a model, called with the secret key at https://<ref>.<zone>/gpu/v1/servers/<name>/: the first request starts it (503 meanwhile) and it stops after its idle minutes. stop_gpu_server and delete_gpu_server stop or remove it. create_gpu_volume makes GPU storage in one data center; give it to a job or server as volume and it is mounted at /workspace, so a model is downloaded once (billed by the GB-month while it exists; delete_gpu_volume deletes it). region (auto, asia, us, eu) chooses where to rent; with storage, the GPU is rented in its data center. The closest to Korea is AP-JP-1 (Japan). The image, command, environment variables and stored files are processed in RunPod's data centers. Prices and how many run are in list_gpu. From ₩4,500 an hour for an RTX 4090, billed by the second from when the GPU is rented until it is let go, image download included.
  • MySQL databases (WordPress): create_mysql_database makes a MariaDB 11.4 database and its own login (usually within a minute). With app only that app gets it; without, it is the whole project's (while it is the only one, every app gets it). bind_mysql_database changes which app gets it. That app's servers start with MYSQL_URL, MYSQL_HOST, MYSQL_PORT, MYSQL_USER, MYSQL_PASSWORD, MYSQL_DATABASE and, as the official WordPress image reads them, WORDPRESS_DB_HOST (host:port), WORDPRESS_DB_USER, WORDPRESS_DB_PASSWORD, WORDPRESS_DB_NAME. Never in a build, and a variable saved under the same name wins. Previews get the same database. The address is reachable only from apps on this server. get_mysql_connection shows how to connect; list_mysql_databases lists them with the plan's limit (Free 1, Pro 10, Enterprise 50). Backed up every night; create_mysql_backup, list_mysql_backups and restore_mysql_backup back up now and put one back (the state just before is backed up first). delete_mysql_database deletes the database, its login and every backup. Deleting and restoring take the database name exactly in confirm. REST: /mysql, /mysql/<name>, /mysql/<name>/connection, /mysql/<name>/backups, /mysql/<name>/restore.
  • Phone calls and websockets: a server deployment takes websockets. Point a Twilio number's voice webhook at https://<address>/voice and the call's audio streams in live (starter: examples/phone-voice in the repository). While a connection is open the server is left out of the idle stop and of making room, so a long call is not cut. Keep a main number on the production deployment: waking a stopped server makes the first call a few seconds late.
  • Server memory is 512 MB (one CPU) by default; choose 1024, 2048 or 4096 MB with set_server_memory (or the console's Deployments → Build settings; the current size and a month's cost are in get_server_memory). Above 1024 MB a server gets two CPUs. The memory held is metered by the hour as runtime_gb_hour. Each plan sets the largest size it allows. Servers at production and branch addresses always run, requests or not (minimum one instance; there is no switch for it), so the month in get_server_memory is what one server costs a month.
  • Concurrent builds: how many of this project's builds run at once, 1 to 20 with set_concurrent_builds, or null for no limit (the console's Deployments → Build settings, right below server memory; the current value is in get_concurrent_builds). Builds over the limit wait in the queue, and a waiting build is not billed for build minutes.
  • Dockerfile: a project folder with a Dockerfile and no package.json (Python, Go, any language) is built from that Dockerfile and run as an image. A repository that also has a package.json asks for the image by choosing the docker preset or "framework": "docker" in blackwall.json. It starts with the image's CMD and listens on the port the image EXPOSEs (the lowest TCP port, or 3000 if none); set another with port on update_app. Whatever USER the image names, it runs as an unprivileged user (uid 10001) on a read-only file system. It can write to /tmp (64 MB), to /run, /var/run/apache2 and /var/lock/apache2 (16 MB each, empty after a restart), to the paths listed in blackwall.json's "writablePaths" (up to 8, 16 MB each), and to its volumes. Keep files that must last on a volume (below). It may listen on a port below 1024, such as 80. An image that must start as root, like the official WordPress image, needs "Run as the image user" turned on in the build settings (run_as: "image" on update_app). It then runs as the image's USER, root included, inside a gVisor sandbox, so even as root it cannot reach other projects. On a server without gVisor, turning it on is refused with a message saying so. Build-time variables reach the build as --build-arg and stay in the image's history, so keep secrets in the server's variables. Each build step gets 2 GB and 2 CPUs, a build at most 45 minutes, and an image at most 4 GB.
  • /data: a server that is not a Dockerfile build (Node and the like) can write to /data as it is, because SDKs written for Railway and Fly keep their cache and tokens there. It holds up to an eighth of the server's memory (16 MB to 256 MB) and is empty after a restart. To keep what is in it, attach a volume at /data (below), which takes its place.
  • Volumes are disk that stays when a server restarts or a new deployment goes out. Add one to an app's production servers with create_volume (WordPress: /var/www/html), see them with list_volumes, grow one with resize_volume (it cannot shrink), and remove one with delete_volume (its files and backups are gone and it cannot be undone, so confirm takes its path). create_volume_backup packs the files and keeps them 30 days; restore_volume_backup puts one back (the files it replaces are backed up for 7 days first). A volume is 1 to 500 GB and its size is a hard cap: past it, writes fail. The plan sets what a project's volumes may hold together (Free 1 GB, Pro 50 GB). Previews, and older deployments production no longer serves, get none. Adding or removing one restarts the servers within a minute. The console has the same under Deployments → Build settings.
  • Changed settings apply from the next push. Only owners and admins can change them.

Git connection: importing, private repositories and GitLab

The same things as Deployments → Git connection in the console.

list_git_access               connected repositories (connected), GitHub accounts, saved tokens (last four characters)
list_importable_repositories  find repositories the GitHub App can see (query searches all, refresh asks again)
import_repository             connect a repository and start its first deployment (with build settings and dotenv)
disconnect_repository         disconnect one repository: pushes stop deploying, history stays
set_checks_on_server          Run checks on One Server: a GitHub repository's Actions jobs on One Server's machines (on/off)
add_repository                connect a repository before the first push (github: owner/name, gitlab: group/name)
set_repo_token                a token to fetch a private repository (gitlab_base for self-hosted GitLab)
remove_repo_token             delete a token
link_github_app               link the GitHub account another project of the organisation already linked
unlink_github_app             disconnect one GitHub account (App installation), or all of them

Connecting a GitHub account needs a person to confirm it on GitHub, so it can only be done in the console under Deployments → Git connection. Importing after that works through MCP. If a private repository's build fails at the fetch step, start with list_git_access. Only owners and admins can change any of this.

Import from another server

The same as Import from another server under the organisation in the console. Source accounts (Supabase, Vercel, PostgreSQL) are connected once in the console, so a token is never pasted into a chat. Supabase connects with [Sign in with Supabase]: press Authorize, nothing to copy.

import_sources                connected source accounts, their projects, and imports so far
start_import                  start an import (one split in the console, or connection+project with one new project per group)
get_import                    each destination's progress by step, what is not moved and why
copy_auth_from_import         copy a Supabase project's sign-in settings (social providers, redirect addresses) into a project here
set_vault_project_keys       write this project's own address and secret key to its vault (they move inside the server)
copy_vault_secrets_from_project  copy another project's vault secrets, values included (values move inside the server)
copy_jobs_from_project        copy another project's scheduled jobs, switched off
copy_auth_from_project        copy another project's sign-in settings into this one (secrets move inside the server)
copy_storage_from_project     copy another project's buckets and files into this project (same size: passed over)
copy_storage_from_import      copy a Supabase project's buckets and files into a project here (same size: passed over)

A Supabase source needs its session pooler connection string in start_import's address. It is used for that run only and then deleted. Scheduled jobs arrive switched off. Over REST: GET /v1/imports/sources, POST /v1/imports {org, import, connection, project, address} and GET /v1/imports/<id>.

To move only the sign-in settings into a project that already exists, use copy_auth_from_import. Secrets are read from Supabase with the connected account and stored, never put in the answer. See what would move with dry_run first, and afterwards register the answer's callback_url in each provider's console. Sign-in fails until it is registered. Over REST: POST /v1/projects/<ref>/config/auth/copy-from-import {connection, project, providers, dry_run}.

To move only the files, use copy_storage_from_import. A bucket missing here is made with Supabase's public flag and limits, and a file already here with the same size is passed over. One call copies up to 200 files within 45 seconds; when files remain, call again with the answer's next until done is true. The Supabase key is used inside the server only and never put in the answer. Bucket access rules are not copied. Over REST: POST /v1/projects/<ref>/storage/copy-from-import {connection, project, buckets, rename, dry_run, next, max_files}.

Deploying without a push

redeploy             rebuild the same commit (when you added environment variables later)
deploy_commit        deploy a specific commit
promote_deployment   a finished preview build, to production
rollback_deployment  production, back to an earlier production deployment (no rebuild; holds production)
create_deploy_hook   an address that, when called, builds the branch's latest commit (CMS publishing, etc.)
list_deploy_hooks    those addresses
remove_deploy_hook   delete an address

If you changed environment variables, you need a redeploy. Values baked into a build change only when it is rebuilt. Promoting a preview to production rebuilds it with the production environment variables. The preview build is not reused as it is. Only owners and admins can deploy to production.

Deploying without a repository

deploy_files    send files (path to content), build and deploy them (up to 3MB in total; more: create_source_upload_url → deploy_source)
deploy_source   deploy an archive already uploaded (source)
create_source_upload_url   an address to PUT a large archive to without the token (1 hour)
  • They go through the same build as a pushed commit. With a package.json the framework is detected and built; with only index.html it is served as it is.
  • With prebuilt: true nothing is built and the files are served as sent (an already built dist/).
  • target is preview (the default) or production. A preview address comes from branch (preview by default). A project with no app gets a site app.
  • For a large site use oneend deploy (packs the folder and uploads it) or POST /deployments/sources (body: .zip, .tar, .tar.gz, up to 50MB), then deploy the source it returns.
  • With tools alone, get an address with create_source_upload_url, PUT the archive to it (curl -X PUT --data-binary @site.zip '<url>'), and call deploy_source with the source in the answer. The address works for that project for 1 hour, with the same size limit and checks as POST /deployments/sources. Over REST it is POST /v1/projects/<ref>/deployments/sources/upload-urls.
  • node_modules and .git are left out. Paths or links pointing outside the folder, and archives that would be too large unpacked, are refused.

More build settings, system variables, deployment security

get_build_settings        per app: development command, Node.js version, ignored build step, production first, required checks
set_build_dev_settings    development command (oneend dev) and Node.js version (18 | 20 | 22 | 24)
set_ignored_build_step    which pushes are built (automatic | every | production | preview | folder | command)
set_production_first      production builds first in the queue
set_git_checks            required GitHub checks, Git LFS, verified commits only
set_expose_system_env     set or leave out system variables such as BLACKWALL_ENV and BLACKWALL_URL
set_directory_listing     list the files of a folder with no index.html
get_deployment_security   who sees build logs and output, fork protection, OIDC
set_deployment_security   change those (what is left out stays)

The framework, the build, install and output commands and the build machine are update_app. Only owners and admins can change any of these.

Deployment retention and restoring

get_deployment_retention   how long each kind of deployment is kept (days in force, the project's number, the plan default)
set_deployment_retention   change it (null for the plan default; project admins)
list_deleted_deployments   deployments deleted in the last 30 days, and until when each can be restored
delete_deployment          delete a deployment (restorable for 3 days)
restore_deployment         restore a deleted deployment
  • Plan defaults: Free keeps every kind 30 days. Pro and Enterprise keep canceled 30 days, failed 90, preview 180 and production 1 year (365). A project's own number wins.
  • Kept whatever the period: the 10 newest deployments, the 20 newest ready production deployments, the 20 newest ready deployments that are not production, and anything production, a branch or a domain is serving now (including each branch's latest preview).
  • A deployment you delete, or one deleted because its period ended, can be restored with restore_deployment for 3 days. Meanwhile its dpl-… address answers 410; after 3 days its files and build log are removed and it cannot come back.
  • Over REST: GET·PUT /v1/projects/<ref>/deployment-retention, GET /v1/projects/<ref>/deployments/deleted, DELETE /v1/projects/<ref>/deployments/<id>, POST /v1/projects/<ref>/deployments/<id>/restore. In the CLI: oneend deployments retention, oneend deployments deleted, oneend deployments rm, oneend deployments restore.

Addresses, waiting and the security check

  • The tools that make a deployment (deploy_files, deploy_source, deploy_commit, redeploy, promote_deployment), and get_deployment, list_deployments and rollback_deployment, answer with its addresses. url is that deployment's own dpl-… address, production_url the production address, preview_url its branch's preview address. With preview protection on, note says who can open them.
  • After rollback_deployment the app's production is held (production_held, "Production held" in the console): new production builds are built but not served on the production address. promote_deployment with a finished production deployment serves it without a rebuild and lifts the hold; with the one serving now it only lifts the hold. list_deployments lists held apps in production_held.
  • With wait (seconds, up to 120) they wait for the build to finish (ready or error) and answer with the result. A failed build comes with error and the end of build_log; one not finished in time comes with still_running: true and retry_after_seconds, after which get_deployment with wait waits on. wait on create_project waits for the database, and status in list_projects is ready, provisioning, failed or paused.
  • advisors in the answer is the number of critical security problems get_advisors finds (a rule that lets anyone write, a view that skips the rules) and the top 3. By default they are reported and the deploy goes ahead. With block_on_advisors: true a production deploy is refused while there are any (previews are not held back); oneend deploy --block-on-advisors does the same. Deploys started by a git push are not held back.