09 Limits and reference

Chapter 09

Limits and reference

The numbers you will eventually hit, and a table for when something is not behaving.

Limits, PHP settings and paths below describe catalog sites — they come from the platform's three built-in images. Any other image on your box — an administrator can upload one, as chapter 03.1 describes — brings its own versions and its own defaults, and the panel's picker is what tells you it is there. A custom stack brings its own images too, so its runtime, its limits and its timeouts are whatever you put in them. The stack-specific reference is at the end of this page.

Request and upload limits

SettingValueWhat hitting it looks like
Maximum upload size64MLarge uploads rejected by the web server before PHP sees them
POST body size64MSame, for form submissions
Maximum execution time60sLong request killed mid-flight
Request timeout at the web server60sA 504 even if PHP is still working
Maximum input variables5000Very large forms silently truncated
Default PHP memory, per request256MFatal "allowed memory size exhausted" — adjustable per site
Default requests at once8Requests beyond it queue rather than fail — adjustable per site
Session lifetime7200sUsers signed out after two hours idle

Both timeouts are 60 seconds, so anything genuinely long-running belongs in a queue worker rather than a web request. The last two rows are the only ones you can adjust per site, and they are the same two settings chapter 03 describes.

PHP environment

Version
PHP 8.3, served by php-fpm behind nginx.
Extensions
mysql, mbstring, xml, bcmath, curl, zip, gd, redis, opcache, intl
Tooling
Composer, git, unzip, and the PHP CLI are available inside the container.
Timezone
UTC. Set your application's own timezone in its configuration if you need another. This is the clock inside your container; the panel keeps a server timezone of its own, further down.
Error display
Off. Errors are logged to the container output, never rendered to visitors.
Opcache
Enabled, and does not re-check file timestamps. Restart the site after deploying PHP changes.
Concurrency
Eight PHP worker processes per site by default — the "Requests at once" setting — recycled every 500 requests.

What your site is using

Resource usage in the panel's menu lists every site you can see — your own, if you are a site owner — with its memory, CPU, peak and disk, heaviest first, and the server itself on a line of its own. Open a site from that list for its charts, over 6 hours, 24 hours, 7 days or 30 days, and for the one control that lives there: a memory limit.

Readings are taken once a minute, and the page says how old its figures are. "Not measured" and "Unknown" are real answers, not errors to work around — they mean the measurement has not run recently, or that nothing has measured this yet.

Memory, and the second memory figure
Two numbers, and the difference between them catches everyone: the working figure, and a larger one including file cache — pages the kernel is holding on your behalf and would drop under pressure. A limit is enforced against the larger number, so that is the one to compare a limit against.
Peak
The highest reading ever taken, not a guaranteed maximum. A spike shorter than a minute happens between two readings and never appears. Use it to choose a limit; do not read it as proof of what the site can reach.
Disk
It comes from the site's backup — measuring it is a side-effect of taking one — so it is the figure from the last night this site was copied, which is not every night if you have narrowed its backup days (below). A site with backups turned off reads Not measured rather than 0 B, and a site created today shows nothing until the morning after its first backup.
Platform services
The shared MySQL, Redis and Caddy are listed on their own, for administrators, and their usage is not attributed to any site. A query-heavy site inflates MySQL rather than its own row, which is why the site rows never add up to the server total.
CPU, which is two different numbers
A site's CPU is a percentage of one core, so a busy site using several of them legitimately reads over 100%. The server's own figure is the share of the whole machine that is busy, between 0 and 100, and never passes it. Comparing the two directly means nothing.
The server's own line
The machine reports its processor first, then memory in use against total, swap in use against swap total, and disk. The processor figure is an average over the last minute rather than a live reading, and the page says so; memory and swap are live. A server with no swap configured says that in words rather than showing 0 B / 0 B, which would read as a broken measurement. There is no per-site swap figure: swap is a property of the whole server.

Limiting a site's memory

Empty is the default, and empty means no limit. Give it a figure — 512M, 1G — and the server stops the container when it reaches that and starts it again. The point is blast radius: one site cannot exhaust the machine and take every other site down with it.

It applies at the next re-create, not on save

Saving the figure records it. The container keeps running under its old limit until it is recreated — press Recreate, in the Container box above the site's tabs (chapter 02.1), and any change that recreates the container anyway does it too. A restart does not.

Choose the figure from the site's own peak, with headroom above it. A limit that is too small does not warn you: the site is stopped and restarted, again and again, exactly when it is busiest.

The control panel itself has no limit and cannot be given one. That is deliberate — the panel is what you use to fix everything else, so it is never the process that gets stopped.

Reporting to a cockpit

If your operator runs more than one of these servers, they can link this one to a cockpit: a separate page that watches several installations at once and shows each one's version, its processor, memory and disk use, and whether it is still reporting. It is a tab under Settings and only an administrator sees it. On a single installation the tab is there, says what it would do, and can be left alone.

Linking one takes three steps: open the cockpit, create a pairing code there, then paste the cockpit's address and that code into SettingsCockpit. A code works once and then expires, so it is made at the moment it is needed rather than kept somewhere.

What leaves the server is listed on the page itself, rather than summarised:

  • The installation's name, identifier, version and address.
  • The processor, memory, swap and disk figures described above.
  • How many sites there are, and how many containers are running.
No site name, no domain, no address of anything hosted here

A cockpit may watch several people's installations, and a list of sites is a list of customers. Counts go; names do not.

The panel does the calling, always. A cockpit never contacts the server — which is the opposite of the usual assumption about a feature that watches machines. Linking one opens no port and needs no change to a firewall, and nothing at the cockpit end can act on this server: it reads the figures the panel chose to send it, and there is nothing else for it to reach.

A report goes every five minutes, so the cockpit's figures can be up to five minutes behind the ones on this server — worth knowing before you conclude the two disagree. A cockpit that is down changes nothing here: sites, backups and everything else carry on, and the Settings tab says when a report was last accepted and why the last attempt failed. Unlinking works whether the cockpit answers or not, and the panel stops reporting immediately either way.

Which nights your site is backed up

A site is copied at night, in one run at the platform's backup time. Its Backup tab carries a Backup days box above the retention line: seven weekday buttons saying which nights this site may be taken on. Anyone who can open the page can set them, site owners included.

The reason to narrow them is that a site pauses while its copy is taken. Visitors get a "temporarily unavailable" page for a few seconds, and a stack is stopped for that moment. A site whose own nightly jobs run at the same hour, or one nobody wants blipping on a weekday morning, can move to other nights. It is not a way to make a backup faster or smaller, and fewer nights means fewer copies to go back to.

Not set
Where every site starts: all seven buttons selected, a Not set label beside them, and a line saying the site has no days of its own. It follows the platform, and nothing needs doing about it.
Days of its own
Once you save, the label goes and a Back to the default button appears, which puts the site back to following the platform. Saving all seven days looks identical to Not set in the buttons, on purpose — the label is what tells the two apart.
One day is the minimum

Clearing every day and saving is refused. A site with no backup night is never copied, and the copies it already has expire on their own, so the panel does not let a blank form say that by accident. There is no switch here for turning a site's backups off, and that is deliberate.

Nothing else about the run changes. On a night a site is not due, it is simply not in the run: nothing pauses, and nothing appears in its list of copies for that date. There is no skipped entry to look for.

How many copies are kept is a count and not a length of time — the page says how many of the last copies it keeps for this site. Seven copies of a site taken twice a week reach back three and a half weeks; seven copies of a nightly one reach back a week. Read the number as copies and work the span out from the days you chose.

The platform's own copy — the panel's database, its secrets, the certificates — is taken every night whatever any site's days say, and it takes nothing offline while it runs.

The clock everything runs on

Every time the panel prints or schedules is in the server timezone: the backup time and the days above, run history, the resource-usage charts, the emails it sends. It is one setting, on SettingsGeneral, and only an administrator can change it. An installation where nobody ever set one is on UTC, which the page says rather than leaving you to assume.

The same page shows the server time. That is read from the panel and not from the computer you are sitting at, which is what makes it the way to check a change took effect — and it is the clock the backup time refers to.

The pages move before the schedule does

Everything the panel displays follows a new zone straight away. The nightly schedule follows it only once the panel has been restarted, which is the panel owner's job. Expect the two to disagree until that happens.

This is a different thing from the timezone inside your own container, which is UTC and is changed in your application's configuration, as above.

Taking a copy of a backup away

Every site has a Backup tab listing the copies taken of it, and each row offers two things. Restore this copy puts the site back as it was. Download backup gets the copy off the platform entirely, which until now needed a shell on the server. Both are yours to press on your own sites; neither is administrator-only.

Downloading does not hand you a file there and then. It goes like this:

  1. Press Download backup on the copy you want. A dialog says what is about to happen; press OK.
  2. The page comes straight back, and the copy is prepared as a zip in the background. A large site takes a while.
  3. An email with a link arrives when it is ready, and the same link appears on that row of the Backup page. Either one works — the row is there for the day the email goes astray.
The file is the site's secrets, in one place

The zip holds everything that copy holds: your files, the .env, the database dump and the site's TLS private keys. That is what makes it worth having, and it is the warning as well. Treat the file the way you treat a password, keep it where you would keep one, and delete it when you are done.

The link lasts 72 hours. After that the file is deleted from the server and the row stops offering it — not archived, not hidden. If you need it again, ask again; preparing a second copy of the same backup is an ordinary thing to do.

The link is not a secret URL — it is yours

It is tied to the account that asked for it. Signing in as somebody else and opening it does not work, and that includes an administrator, and the site's owner if the owner is not you. The random characters in the filename only stop it being guessed; what admits you is your own session.

It does work from anywhere, deliberately: you should be able to open it wherever you read your email, including from an address the allowlist has never seen (chapter 08).

One preparation runs per site at a time. Asking again while a site is already preparing one is refused, so wait for the email. Two people can each prepare their own copy of the same backup, and each gets their own file.

If it cannot be prepared, an email tells you the download is unavailable at the moment. It does not say why — administrators are told the reason separately, and the usual one is not enough free space on the server, which somebody may have to clear before you try again. Nothing about the site or its backups changes either way.

When a backup fails

Backups are a platform-wide job, and the Backups page belongs to administrators. What matters to everybody else is that a failure is no longer only visible to somebody who thinks to look: every administrator account is emailed once per failed run.

What counts as a failure
A run in which everything failed, and a run in which some things failed while others succeeded. The second is the ordinary case — one site's database dump fails at 03:15 and the other twelve capture fine — and it sends mail exactly as the first does.
What does not
A site that was deferred because it was mid-deploy when the run reached it. That is not a failure: the site is taken on the next run and nobody is emailed.
One email per run
A run with four failed sites sends one message naming all four, not four messages. Re-running a backup by hand produces a second email only because it is a second run.
Who gets it
Every administrator account, worked out when the message is sent. Promote an account and it starts receiving them; demote it and it stops. There is no subscription list and nothing to configure. Site owners and unlock-only accounts are never emailed — a site owner cannot open the Backups page at all.
What is in it
Which installation it came from, when the run started and finished, each failed unit with the reason recorded for it, how many succeeded and how many were skipped, and a link straight to that run.
Sent is not delivered

The panel hands the message to a mail service, and that is the end of what it knows. There is no delivery receipt, no bounce notice and no send history to look through, so the panel can say a notification was sent and can never confirm anybody received it. If an administrator is not getting them, the things to check are the address on their account and their spam folder; nothing in the panel will tell you what became of a particular message.

Off is a normal state, and it changes nothing about the backups

The mail credential lives in the server's own configuration file rather than in the panel, so there is deliberately no field to paste one into and only your operator can switch this on. Until they do, the Settings page says so plainly and backups keep running, failing and recording their failures exactly as before. Nothing about mail can change whether a backup succeeded or what it reported. The same credential carries account invitations (chapter 08) and backup download links, so a server with one has all three and a server without has none of them.

Paths

Site folder
/var/www/virtual/<your-domain>/ — identical inside and outside the container
Docroot, file-serving images
the folder itself — the same for Static Website and Single-Page App
Docroot, PHP image
<folder>/application/public
App root, PHP image
<folder>/application — the deployed app (repo contents, vendor/, artisan); wiped and re-published on each deploy
Durable data, PHP image
<folder>/storage (Laravel storage tree) and <folder>/.env (secrets) — kept outside application/ and symlinked into it, so they survive a re-publish
Background programs
<folder>/application/.nimbus/supervisor/ — the known names boot.conf, queue-worker.conf and scheduler.conf, plus every *.conf in its daemons.d/ subdirectory
A folder mounted from another site
Wherever the mount says, and never inside the site's own folder — /mnt/<something> by habit. Applied when the container is created, so a new one needs a Recreate (chapter 06)
Health endpoint
/health — reserved; do not define a route at this path
SFTP
Port 2223, SSH key only and no password, username is your email address. You get one directory per site you hold, worked out at connect time, and the port is not behind the IP allowlist (chapter 04)

Service addresses

MySQL
mysql:3306 from a catalog site, mysql.nimbus:3306 from a stack — version 8.0. Provision the database and user yourself from the site's Databases tab.
Redis
redis:6379 from a catalog site, redis.nimbus:6379 from a stack — version 7. Persistent, does not evict; set expiry on cache entries.
Your site
no-<domain-with-dashes> on the internal network, port 80. No published host ports.
Your stack
Containers named stk-<slug>-<service>-N, on the project's own network. The ports you declare as ${NIMBUS_BIND}:${NIMBUS_PORT_*} are reachable from the edge and nowhere else. A port an administrator has published on the server is the one exception, and it is reachable by anyone.

Your box states these itself, on System informationTechnical — the two host pairs, the site and stack folders, and the certificate authority it issues from. Where this page gives the platform's defaults, that one gives your box's actual values. Chapter 08.

Custom stacks

Compose file
compose.yaml or docker-compose.yml, at the repository root. Validated on every deploy.
Project name
stk-<slug>, owned by the panel. Prefixes every container, network, volume and locally-built image.
Stack directory
/var/www/stacks/<slug>/application/ (the clone), .env (symlinked into it), .nimbus/override.yml (panel-owned)
Allocated ports
Declared in the compose file as "${NIMBUS_BIND:-127.0.0.1}:${NIMBUS_PORT_<NAME>:-8080}:<container-port>" — anything else is refused. Allocations are per name, sticky across deploys, released on delete. Reachable from the edge proxy only.
Published ports
A host port forwarded straight to a service's container port, added by an administrator on the Ports page. Host port from 2200–2299 by default; stacks only; no TLS and no IP allowlist, because the edge proxy is not in the path. Adding or removing one recreates that service. Chapter 02.2.
Injected at deploy
COMPOSE_PROJECT_NAME, NIMBUS_BIND, one NIMBUS_PORT_* per name, and extra_hosts for mysql.nimbus / redis.nimbus
Not permitted
container_name, top-level name, external volumes, host network_mode/pid/ipc/userns_mode, privileged/cap_add/devices/security_opt, bind mounts outside your application/, variables in a volume source

The HTTP API

One endpoint is documented for you to call directly: the one that unlocks the address a call arrives from. It is what makes an unlock possible from a machine, on a timer, with no browser involved — chapter 08 covers when you would want that and how to get a token for it.

Endpoint
PUT /api/v1/allowlist/address, on the panel's own domain. That domain is never lockable, so it answers from anywhere.
Authentication
Authorization: Bearer <token>. Tokens are issued on Your accountAPI tokens, by any account whatever its role, and shown once.
Body
{"label":"Home office"}, sent as application/json. label is required and is the name the address appears under on Unlocked IPs.
Which address it unlocks
The one the request came from, always. There is no address field, and supplying one anywhere in the request changes nothing.
Rate limit
10 calls a minute from one address.
AnswerWhat it meansWhat to do
200 · "enforced": true The address is on the list and in force at the proxy. Nothing. Locked sites serve you immediately.
200 · "enforced": false Saved, but not yet in force — the proxy has not taken it. Repeat the same call. A script that checks only the status code will report success while the site still refuses you.
401 The token is wrong, or it has been revoked. The same answer is given for every such case, deliberately. Issue a new token. Do not expect the reply to say which it was.
429 More than ten calls a minute from your address. Call less often. A quarter-hourly timer is nowhere near this.

Troubleshooting

SymptomMost likely cause
Deployed a fix, old behaviour persists Opcache. Restart the site — see chapter 04.
Edited a Blade template, old markup still renders Also opcache. The compiled template keeps the same filename, so the cached copy wins. Restart the site.
Page says "image OK (no app deployed)" Nothing at the docroot. On the PHP image your entry point must be at application/public/index.php.
Status is Up but the site errors Expected — the health check never runs your app. Check the container logs.
Site restarts by itself when it gets busy Its memory limit is too small, so the server stops the container and starts it again. Raise it above the site's peak — see above.
A folder from another site is not there, or is empty Either the mount has not been applied yet — that takes a Recreate, not a restart — or its source is gone, which the site's Mounts tab reports as Source missing with the path it looked for. Nothing is ever created in place of a missing source. Chapter 06.
Writing to a mounted folder fails The mount is read-only, which is the default. Only the site that produces the files is given write access, and changing that is an administrator's call.
Deleting a site is refused Another site mounts a folder of it. The message names them; those mounts come off first — chapter 06.
Blank 403, no styling Your IP address is not unlocked. See chapter 08.
The panel's front page offers no way in, only "Unlock my address" Working. You are at an address that is not unlocked, so the button that would refuse you is withheld and the one that fixes it is shown. Press it — chapter 08.
A site you know exists shows "not found" It is not filed under your account. Another owner's site is absent from your panel rather than marked private, so it reads exactly like a deleted one — chapter 08.
An unlock call succeeds nightly but the site still refuses that machine The reply is carrying "enforced": false and the script is only reading the status code. Repeat the call and check the field — see above.
An unlock call that worked yesterday answers 401 The token was revoked, or the account holding it was deleted. Either also closed the address it had opened. Issue a new token.
SFTP fails naming a host you have never heard of The @ in your username was not quoted, so the client took the part before it as a machine name. Write 'you@example.com'@your-serverchapter 04.
SFTP refuses your password There is no password to refuse. The service takes an SSH key and nothing else; add the public half on Your accountSSH keys.
The Users, Backups or Settings pages refuse you Those are administrator-only. A site owner gets their sites and nothing platform-wide; the refusal is a styled panel page, not a blank 403.
Certificate warning or HTTPS failure after publishing DNS not resolving to the box yet, so the certificate is still pending. On a dev box, an untrusted authority is normal.
Every visitor shares one rate limit Trusted proxies not configured, so every request carries the proxy's address. See chapter 07.
Every log entry shows the same IP address The same cause, and the same fix — the address being recorded is the proxy's.
Generated links and redirects come out http:// Also trusted proxies: the app cannot see X-Forwarded-Proto, so it builds URLs from the plain HTTP it was handed.
App cannot connect to the database The database was probably never provisioned — do it from the site's Databases tab. From a stack, also check the host is mysql.nimbus and not mysql.
Stack deploy refused with a message about the compose file Your repository uses a construct the platform does not allow, or publishes a port in the wrong shape. The message names the service and the key — see chapter 02.2.
Stack domain resolves but serves nothing Almost always a port bound to literal 127.0.0.1 instead of ${NIMBUS_BIND}. Caddy is a container and cannot reach the host's loopback.
Nothing answers on a stack's published port The panel opens the port; the service still has to listen on it and, for something like Gitea, be told to advertise it. Check the service's own configuration and redeploy — chapter 02.2.
Queue worker not running Filename must be exactly queue-worker.conf; then restart the site. Check the log for a SKIPPED line.
A program of your own never starts It has to be a *.conf file inside .nimbus/supervisor/daemons.d/ — loose in .nimbus/supervisor/ under a name the image does not know, it is ignored without a word. Then restart the site. If two files declare the same [program:…] name, only one of them runs.
Worker rejected on start Invalid INI, a non-program section, or user= set to something other than www-data.
Uploads fail over ~64 MB The upload limit. Not adjustable per site.
Request dies at 60 seconds Execution or gateway timeout. Move the work to a queue.
Assets stale after deploy Static files carry a one-year immutable cache header. Fingerprint asset filenames.

What this platform does not do

Stated plainly, so you can plan around it rather than searching for a setting that is not there:

  • No build step for a catalog site. Files are copied in and the site is restarted — or the in-container runner does it on push (chapter 10). Building images is a stack's job.
  • No deploy-on-push for a stack. Deploys are a button in the panel, because they build images and the runner deliberately cannot reach Docker.
  • No renaming a stack. The slug is baked into every path and label — delete and re-create.
  • No custom environment variables beyond the settings an image declares. Your own configuration goes in the site's .env.
  • No wildcard certificates. Every domain needs its own DNS record.
  • No say over what is backed up or how long a copy is kept — retention and storage are settings on the server, and administrators hold them. What you can do with a copy of your own site is restore it or download it, as above. Deleting a stack still destroys its volumes with no undo.
  • No self-service sign-up and no password reset. An administrator creates every account, either typing a password or emailing an invitation the person uses to set their own (chapter 08). The platform sends three kinds of email and no others: an invitation, the failed-backup notice above, and the download link above that.
  • No log viewer in the panel, except a stack's deploy output. Container logs are read from the box.
Designed but not built

The project has a design for automatic banning of probing addresses. It does not exist in the running platform. If you come across it in the project's design documents, read it as intent rather than as behaviour you can rely on.