Running your project
Staging & Testing
What this page is for: a staging site is a private copy of a project where you try changes safely before they go live. It is on an address of its own, so you can try an update, a plugin, a theme or a big change there first. The copy lives inside the project it was made from: the project's own Staging & Testing page lists its copies. When you are done, delete it: everything it had is gone. (The page was called Staging before, and earlier versions called a staging site a test copy.)
You need
- An address for the copy — one of:
- a temporary address (
https://…sixpanel.dev), which needs this panel connected to your AllsWeb account (Settings → AllsWeb account): AllsWeb creates and deletes its DNS record for you; or - your own domain (
staging.example.com), which needs no AllsWeb account. - An email address for certificates (Settings), the same one every HTTPS address on this server uses.
- Enough free disk space for a second copy of the project's files and database. The panel checks before it starts and says how much it needs.
Which projects can have a staging site
The four kinds the panel can start by itself:
| Project | Staging site |
|---|---|
| 6amMart shop | Yes — everything is automatic |
| WordPress website | Yes |
Laravel app (a PHP website with an artisan file and its packages installed) | Yes, when its database is on this panel |
| Static website | Yes |
| A custom app — another PHP app, a Node.js app | Not from this page. AI Coding can make one and set it up, and you start it — or you set up a second website by hand. See A custom app: no staging site |
| A Create with AI website | No. See A custom app: no staging site for what to do instead |
One rule holds for every staging site: it never reaches production's database, files, queue or customers. The panel does not take that on trust. Before a staging site answers a single request, the panel asks the app itself which database it uses, and starts it only when the answer is the staging site's own. An app the panel cannot ask is never started by the panel: this page offers it no staging site, and one that AI Coding sets up is started by your own press, nobody else's.
How to use a staging site, in short
- Open the project, then Staging & Testing, and press Create staging site. Choose a temporary address or your own domain, press the button, and wait for the job.
- The panel copies production's database and files, points the copy at its own database, switches its outgoing email off (and, for 6amMart, payments, SMS and push notifications), asks the app which database it uses, and starts it. Its card then says Started by the panel.
- Press Open and test: install the update, the plugin, the theme; place a test order.
- Want production's newest data in it? Press Refresh from production. What you changed on the staging site is replaced.
- Take what you tested to production. With git: Promote (Working on a branch and promoting). Without git: make the same change on production — the same ZIP, the same update (Without git).
- Done? Press Delete staging site.
If a card says Not started, it also says why and what to do. Usually: put right what it names and press Look again. Nothing you press can start a staging site that still points at production.
The Staging & Testing page
Every project has a Staging & Testing page in its own menu. For a custom app that page has no Create staging site: it says why, what to do instead by hand, and — for a PHP or Node.js app — that AI Coding can set one up, with the link to it.
- On the production project it lists that project's copies, one card each: the copy's name and label, where it stands (being made, ready, refreshing…), every address it answers on, when it was made and last refreshed, and its switches — with Open, Refresh from production, Copy settings and Delete staging site. Create staging site is at the top.
- Each card also says whether the copy is started. A copy that is not carries the mark Not started beside its name, with the reason, and has no Open (its address would only say it has not been started). A started copy shows one quiet line — "Started by the panel" (or, on a staging site of a custom app, "Started by you on" a day or "Running since before the last update") — and what the panel found is folded under it.
- On a copy the same page shows that one copy: what it is a copy of, the same card, and Open production.
On Projects and on Home a copy is not a card of its own: it is a row inside the card of the project it was made from — its short name (which opens the copy), where it stands, and Open. Making one is done inside the project: its Staging & Testing page, and its Overview, which has Create staging site and a link to this page beside it. (A custom app's Overview has the link only.)
A copy's card also has Connect your own AI agent to this copy: it opens that copy's own AI agent page, so the connection made there reaches the copy and nothing else — see Connect your own agent.
Make a copy
- Open the project, then Staging & Testing (or its Overview).
- Press Create staging site.
- The dialog shows what is copied and the copy's short name (the project's name with
-t1,-t2… on the end), and asks where the copy answers — see the next section. Choose the switches (see below), give it a label if you like ("checkout redesign"), and press Create staging site. - The log opens and shows each step. A small website takes a minute or two; a 6amMart shop with a large database and its customer website a few minutes more.
The dialog also says when it starts, for the kind of project you are copying: what the panel asks the app before it starts the copy.
When it finishes, the copy is on the project's Staging & Testing page and in its card on Projects, and every page of the copy says Staging site of … — changes here never touch production at the top, with its address. While a copy is not started that banner says — not started instead, with the reason and a link to its Staging & Testing page.
Where the copy answers
A temporary address. AllsWeb gives the copy a name under sixpanel.dev — one for a website; for a 6amMart shop, dash-… for the admin, plus a name for the customer website and one for live updates when the shop has them. There is nothing to set up, and the name is deleted with the copy. When this panel is not connected to your AllsWeb account the choice is still shown, greyed out, with the link to connect it.
Your own domain. Type the name the copy should answer on:
- a website: one name, for example
staging.example.com; - a 6amMart shop: a name for the admin panel (
dash-staging.example.com), and a name for the customer website when the shop has one (staging.example.com). Live updates get a name of their own beside the admin panel's —ws-staging.example.com— which the panel composes and sets up; you do not type it.
The name is connected exactly as any domain on this server is (see Domain & SSL, and for a website its Domain page):
- With your Cloudflare account added to the panel and the domain in it, the panel creates the DNS record for you — proxied (orange cloud), like every record it makes. Otherwise create the record yourself, pointing at this server, before you press the button: a name that does not reach this server yet stops the copy with the same message the Domain page gives, and Refresh from production on the copy finishes it once the name points here.
- The certificate is Let's Encrypt's.
- A name must be free: not one another project or website on this server answers on, and not the panel's own.
A copy on your own domain is still a copy: search engines are told not to index it and nothing on it is served from the server's cache.
Later, on an existing copy. A copy made on a temporary address can get your own domain afterwards: add it on the copy's own Domain page (press Use your own domain on its card), connect it, and make it the primary. The copy then answers on both. Give the temporary address back on its card removes the temporary name, issues the certificate again without it and has AllsWeb delete it — the copy keeps your domain. Removing the temporary name on the Domain page does the same. A copy always keeps one address: the last name it answers on cannot be removed — add another domain first, or delete the copy.
What a copy is
A copy is a whole project of its own, not a view of the original:
- its own database — a copy of production's, taken as one consistent snapshot (production is never locked or paused while it is read). A Laravel app whose database is not on this panel is offered no staging site: add its database on the website's Database page first;
- its own files — production's as they are live, with the dependencies and build output made again on the copy (
node_modules, the Next.js build, Laravel's caches and logs are not copied); - its own account, PHP pool, Redis, services, scheduled tasks and certificate;
- its own address: one name for a website; for a 6amMart shop, a name for the admin, plus a name for the customer website and one for live updates when the shop has them (see Where the copy answers above).
What the panel changes in a copy
What it does to point the copy at its own database depends on the kind of project:
| Kind | What the panel changes |
|---|---|
| 6amMart | It writes the copy's own database, cache and address into the copy's .env before the copy's files are put in place. The customer website is built again for the copy's address, so its pages call the copy, never the shop. |
| WordPress | It sets the database constants in wp-config.php and points WordPress at the copy's address (every address WordPress stored). Production's object cache is not shared: wp-content/object-cache.php — the file a Redis or Memcached cache plugin installs — is not copied, and the copy's cache keys get a prefix of their own (WP_CACHE_KEY_SALT, WP_REDIS_PREFIX). To test with such a cache, switch it on again in the staging site's own plugin page. |
| Laravel app | In every text file under 2 MB it replaces production's database password, user and name with the copy's — the exact strings, and nothing else. Production's address is replaced only in .env files; every other file that names it is counted and shown, not touched. |
| Static website | Nothing: it has no database. Its pages may still call production's API from the staging address — the panel cannot see or change what a page calls. |
Changing a file is not the same as knowing the app uses it. A .env on the disk proves nothing — the panel writes one into every PHP website itself. That is why a copy is started only on the app's own word.
Started, or not started
Every staging site is one of the two, and its card says which.
Not started means:
- its address answers one sentence — This staging site has not been started. (or …is being made, …is being refreshed, …is being updated) — whatever is on its disk. Only the certificate check still gets through, so a copy on your own domain can get its certificate while it waits;
- nothing of its app runs: no PHP, no Node.js process, no scheduled task, no deploy command, no WP-CLI, no queue worker;
- its Terminal and SSH still work — that is how you put right what its card names. They open with a warning, and an AI agent's guide begins with it: "NOT STARTED: this app may still be connected to PRODUCTION's database. Run nothing of it until its owner has started the staging site."
A copy is born not started. A job that stops half-way, or a panel that restarts, leaves it exactly so.
Started by the panel — four kinds, and only on the app's own word
| Kind | What the panel asks before it starts the copy |
|---|---|
| 6amMart | The copy's own code, run as the copy's account: every database connection, the Redis it uses, the queue, the file store and its bucket, the mailer. Anything that is not the copy's stops the job. |
| WordPress | WP-CLI reads DB_NAME, DB_USER, DB_PASSWORD and DB_HOST out of wp-config.php without loading WordPress; there must be no wp-content/db.php; only then is the database itself asked which one it is. |
Laravel (an artisan file and vendor/laravel/framework) | Its default connection's database and user are the copy's, then the database itself says so; its queue, default disk, cache and Redis name nothing outside this server. |
| Static website | Nothing to ask: it has no database on the panel. |
For WordPress, Laravel and a static site one more thing must be true: the panel searched the copy's whole folder — every folder, every file name, binary files too — and found production's database password nowhere.
If any of that fails, the copy stays not started and its card says why: the app could not be asked, it named another database, it names a queue or a disk outside this server, a file still holds production's password. Put right what the card names and press Look again, or press Refresh from production. No press of yours starts a staging site of these four kinds — only the app's own answer does.
A custom app: no staging site
Another PHP app, a Node.js app and a Create with AI website are not offered a staging site on the Staging & Testing page. Their Staging & Testing page says so, with the reason, and has no Create staging site. Tia, a connected AI agent and the sixpanel command get the same answer.
Why: such an app keeps its database settings wherever its author chose — a file of any name, a variable, a line of code, a value it decrypts when it starts — and there is nothing the panel can ask it. A copy the panel had made half-way is exactly the thing that ends up connected to production's database. So the panel never makes one and starts it by itself.
That leaves two roads, and they are different:
| Road | What you get |
|---|---|
| By hand | No staging site. A second website of your own, which you set up yourself — the five steps below. |
| AI Coding (a PHP or Node.js app) | A staging site, made and set up by AI Coding — and started by you: you press Start the staging site. See AI Coding sets one up. |
A Create with AI website has the first road only: it has no AI Coding page — its Build with AI page, with its own preview and Publish, is where its changes are tried.
By hand: a second website of your own, set up by you.
- Open Projects and press Create a new project. Add a website of the same kind, on an address of its own (
staging.example.com), with a database of its own. - Put the code on it: connect the same git repository (another branch, if you like), or upload a ZIP of the code.
- Copy the data: on production's Database page press Export now and download the file; on the new website's Database page press Import and choose it.
- Give the app the new website's database — its name, user and password are on its Database page — in the place where your app keeps them: its
.env, its config file, its variables. Change the keys it holds for email, SMS, payments and storage to test values. - Test there. When it works, make the same change on production: deploy the same commit, or the same ZIP.
Nothing ties that website to production. It is not refreshed from it, not promoted to it and not deleted with it: it is a website of its own.
The same answer is given to a website whose database is not on this panel: a staging site would get no database of its own, so whatever its code connects to is what production connects to. Add the database on the website's Database page first. (A PHP or Node.js custom app whose database is not on this panel can still get a staging site from AI Coding — with no database of its own. Its set-up begins with exactly that warning, and a second database has to be made where production's is before you start it.)
AI Coding sets one up
For another PHP app or a Node.js app, AI Coding (in the project's own menu; it needs SixPanel Pro) works on a staging site first, and makes one when the app has none:
- Open the project's AI Coding page, leave Staging site chosen, and write what you want changed. With no staging site yet, AI Coding says it will make one. It gets a temporary address when your AllsWeb account gives one — otherwise you are asked for a domain of your own — and its own git branch when production's code comes from a repository.
- The staging site is made not started: the panel copies the files and the database, and its address answers This staging site has not been started. Nothing of the app runs.
- The session's first job is the set-up. It is handed what the card Set up by hand shows you — the staging site's own database (never a password in the text), the files the panel changed, and everything that still points at production — puts the staging site's database where your app keeps it, and asks the panel to look again. It is told to run nothing of the app while it does.
- When the panel's search of the whole folder, and of every variable, deploy command and scheduled task, finds nothing that still points at production, the session shows Start the staging site. That press is yours. It is the same job as on the Staging & Testing page: the panel searches once more, and refuses with the list if anything still points at production. The dialog asks for the same word as the tick on the card: start it only if the app uses this staging site's own database and no other. Where the panel could not look, the session shows no Start — the app's database is not on this panel, or production's password is one a search cannot find. The session tells you what comes first (a second database made where production's is, or your own look through the app's settings), and you start the staging site on its Set up by hand card, with the tick.
- Then what you asked for is done on the staging site.
What stays true whoever set it up:
- Only you start it. Not AI Coding, not Tia, not a connected AI agent, not the
sixpanelcommand, not a temporary login. Asking the panel to look again is the one thing a session may ask for, and a look never starts a staging site of a custom app. - It is on the project's Staging & Testing page like any other staging site, with the card Set up by hand under it — which says that AI Coding made it, and links to the project's AI Coding page. You can go through the five parts yourself instead, and press Start the staging site there.
- What the panel cannot see, the session cannot promise: a password the app keeps encrypted or loads from somewhere else, and its keys for email, SMS, payments and storage. The session is told to look for them and to tell you what it found and where. Read that before you start the staging site, and before you test anything that sends mail or takes money.
- Refresh from production, Delete staging site and — on a branch — Promote work on it as on any staging site.
Who may start one
A staging site of the four kinds: the panel, on the app's own answer. A staging site of a custom app — one AI Coding set up, or one made on an earlier version: its owner, with the press Start the staging site. Look again may be pressed by anyone who may read the staging site.
Staging sites of a custom app: set up, then started by you
A new staging site of a custom app is made by AI Coding and from nowhere else — see A custom app: no staging site. This section is about the ones that exist: one AI Coding made, and one made on an earlier version.
Earlier versions made a staging site of any website. The ones that exist are kept: the project's Staging & Testing page still lists them, and says where another one is made. The panel never starts them. They are searched, and started on their owner's word, as this section describes, until you delete them. Not AI Coding, not Tia, not a connected AI agent, not the sixpanel command inside the copy and not a temporary login can start one.
The same card is shown for a staging site whose app has stopped being one the panel can ask — a Laravel app whose artisan file or packages were removed, for example.
Set up by hand
The card Set up by hand sits under the copy's own card on the Staging & Testing page. It has five parts; work through them in order.
1. This staging site's database. Its name, user, host, socket and port, each with a Copy button, and Show password (the password is fetched only when you press it). Put these where the app keeps its database settings. If production's database is not on this panel the card says so instead: "This app's database is not on this panel, so this staging site has none from the panel. If its code connects to one, it would connect to the SAME one as production." Make a second database where production's is, and give its address to the staging site before you start it.
2. Changed for this staging site. The files in which the panel put the copy's database. Look at them. If the app keeps its database settings anywhere else, change them there too. Each file is a link to the copy's Files page. For a Node.js app or a Create with AI website the card says that no file was changed: the copy's database is in its variables.
3. Still points at production. Three lists, each entry a link to where it is changed:
- files that still hold production's database password — these refuse a start;
- variables, deploy commands and scheduled tasks that hold production's password, name its database or its address — these refuse a start too;
- then, shown only, how many files name production's database or its address. They decide nothing.
Where the panel could not look, the card says that — never "nothing found": a password it cannot tell from an ordinary word (shorter than eight characters, or the same as the user or the database's name), a database it does not know, or a search that did not finish. A file of a release (Node.js, static, Create with AI) is not put right on Files, because the next deploy replaces it: put the setting into a variable, or correct it in the repository and deploy.
4. What the panel cannot see. A password the app keeps encrypted or loads from somewhere else, and the keys it holds for email, SMS, payments and storage. They are as they were copied from production. Change them in the app before you test anything that uses them.
5. Start it. Tick This app uses this staging site's database and no other and press Start the staging site. The button is lit only once the box is ticked, and is not shown while a job is still changing the copy. The press is a job:
- the panel searches again;
- a password found, a search that did not finish, or a variable, command or task that still points at production refuses the start — with the list and the way on;
- otherwise the copy is started, and its card says Started by you on that day;
- what the create left undone runs now: a Node.js build and its deploy commands, the branch's code where the copy follows a branch, WordPress's change to its new address.
Look again runs the search — and, for the kinds the panel can ask, the questions — without starting anything. Press it after you changed a file.
Your word stands for later versions too. Every refresh and every deploy is searched again (next section), and where the app hides its password, keep that file on Kept between deploys in the copy's Settings.
What is asked again
- Refresh from production. Its first step marks the copy not started. The new files are searched before they are put in place. The files that hold the copy's own database password are carried over — except a settings cache the app made itself (Laravel's
bootstrap/cache/): the app reads its settings from its files again. Then it is started again by the panel's own questions. (A staging site of a custom app, which you started yourself, is started again when the search is clean.) - Every deploy of a copy — a push, Deploy the copy, a ZIP, a roll-back, a restore. The version about to be placed is searched before anything of it runs or reaches the live folder. A password in a file that is not the copy's own refuses it there: "config.php holds production's database password — this staging site would use production's database. Keep the file on the server (Kept between deploys) or take the password out of it." After a deploy of a Laravel copy the app is asked again.
- Every save of a variable, a deploy command or a scheduled task on a copy that holds production's password is refused, with the reason.
- A 6amMart copy does not answer while it changes hands: a refresh, every update of the copy, an import and a restore into it start by marking it not started and end by starting it — after its
.envwas written, "Payments, SMS and push off" ran and the app was asked. A job that stops leaves it not started, and its card says why, lists what was found (the file that holds production's password, or what the app answered) and has Look again, Refresh from production and Delete staging site. The job itself names the way on. After a refresh: put it right on production and refresh again. After an update of the copy: correct the branch it deploys and deploy again — a refresh would only deploy the same commit again. After an import of a database from an older version: run Migrations, then press Look again. An update that leaves the staging site not started is listed as a deploy that failed, never as one that finished. - Look again on a staging site that is answering. The app is asked again. If it now names another database — someone changed its
.envorwp-config.phpby hand — or can no longer be asked at all, the staging site is stopped, and its card says what the app answered. Put the file right and press Look again: it is started again on the app's own answer. (A staging site of a custom app, started by its owner, is stopped by a look only when a file holds production's database password.) - A Look again that waited behind another job. If a refresh or a deploy changed the staging site after you pressed, the job stops with "…was changed by another job… Press it again." It would otherwise build a version it never looked at.
- Scheduled tasks wait. While a staging site is not started, none of its scheduled tasks runs: each row says Waits — this staging site is not started, and Run now is answered with the reason. They run again once it is started.
- Migrations.
sixpanel db migrateand the Migrations button run on a staging site that is not started only when the one reason is that the app could not be asked — an older database that needs its migrations before the app can answer. For any other reason they are refused: a migration runs on whichever database the files name.
A database you put into a staging site yourself
Refresh from production is the way to bring production's data into a staging site. But a file can also be put in by hand — Database → Import, a restore of a backup, sixpanel db import in a shell — and that file is, more often than not, production's own export: its live payment gateways, SMS and push settings, and your customers' phones are in it.
A 6amMart staging site is therefore handed the same care whichever of the three you use:
- it is marked not started before the first statement runs — its addresses answer "This staging site is being updated." and none of its processes run, its queue worker and scheduler included;
- the database as it was is kept first, as for every import;
- after the file has run, Payments, SMS and push off, the S3 and reCAPTCHA rules and the mail switch are applied again to what arrived, by the staging site's own switches;
- the app is asked which database it uses, and only then is the staging site started;
- live updates are the staging site's own again: where it has them, its own address is written back into its settings; where it has none, the switch the file brought with it is turned off — the file says they are on, at production's address.
Database → Import runs as a job. The page answers as soon as your file has arrived, and a card at the top of the Database page shows the import step by step: the staging site stopped, the database kept, your file, and what the file left to do. You can leave the page — the job goes on, and Activity keeps its log.
Steps 3 to 5 are a second job, started by the first the moment the file has run — or has failed: on a shop with many uploads they take a minute or two. The same card shows that job next (its log is named "Staging site: payments and email off again, the app asked, then started"), and the staging site stays not started until it has ended well. If it does not, the reason is on the staging site's card. A file from an older 6amMart may need sixpanel db migrate before the app can answer — run it, then press Look again.
- An import is refused while a job is changing the staging site (a refresh, an update): wait for that job, then run it again. Nothing is imported.
sixpanel db importasks the panel to do steps 1, 3 and 4, and follows that job to its end. If the panel is not running, an import into a staging site is refused before anything is changed — start the panel first. A project that is not a staging site is imported as it always was.- If the import or the restore fails half-way, the staging site stays not started. Look again — and the next update of it — apply the rules of step 3 before they start it, so a staging site is never started on rows nobody made quiet.
- Look again pressed while a file is still being imported does nothing until the file has ended: half a file is not looked at, and an update of the staging site that runs meanwhile does not start it either.
A WordPress staging site is stopped before an import runs — its address says "This staging site is being updated." — because the rows of production's export name production's address: its sign-in page would otherwise send you to production's admin. After the file has run, WordPress is asked which database it uses, moved back to its own address, and only then does the address answer again. If the import fails half-way, the staging site stays not started: press Look again, and the same happens before it answers.
Every time a WordPress staging site is started — when it is made, after a refresh, by Look again — it is moved to its own address before that address is opened. While the move runs, the address still says "has not been started". A WordPress that cannot be asked is not moved and is not started.
A Laravel app: the panel changes no row of an app it does not know. What a file brings — a payment key, a mail server — is yours to switch off before you test.
phpMyAdmin, or any database tool you open yourself, is by hand. An import made there is not seen by the panel, so none of the steps above run. On a staging site, use Database → Import.
Nothing starts a staging site's processes while it is not started
A 6amMart staging site that is not started has no queue worker, scheduler, websocket server or customer website running, and none of them can be started from its Services or Domain & SSL pages, by another job, or after a server restart: the press is answered with the reason and the way on (its card on the Staging & Testing page). They start when the staging site does.
A staging site can be started while its customer website stays stopped: when the storefront was built with production's address in it, the card says so — "The customer website of this staging site is not started", with the files — and its admin side answers. Update the storefront of the staging site, or press Refresh: both build it again for the staging site's own address. Until then nothing else starts it — its Services page and a change of its domain answer with the same reason — because an order placed on that build would be a real order in production. (Where the search of the build did not finish, the card says that instead: press Look again.)
The staging site's own database settings never travel
A file that holds the copy's own database password is the copy's connection file, whatever its name. The panel finds it by what is in it, so there is no list to keep:
- a deploy of the copy does not replace it (the log says when the repository has a different version);
- a refresh carries it over;
- it is never listed as changed on the git card and never committed;
- it is never placed on production. Every deploy of a project that has staging sites — a push, an Update, a ZIP, the deploy inside a promote — searches the version for each copy's database password first and refuses, naming the file. A production that ran on a staging database would lose it at the next refresh.
For the same reason Refresh and Delete look for the copy's password in production's live folder and variables before the copy's database is dropped. If it is there, the job stops with production is running on this staging site's database and drops nothing.
And what arrived from production with the copy — uploads in a folder the panel has no name for, a tracked file that was edited by hand on production — is not a change you made: it is left out of every list and every commit, and the git card counts it under came from production. To bring such a hand edit into the repository, change the file again on the staging site (it is then listed) and commit it there.
Staging sites made before this update
A copy made by an earlier version keeps answering — of whatever kind it is, a custom app's too. The first time the updated panel starts, one job per such copy looks at it:
- production's database password is found in it → the copy is stopped, and its banner says "Stopped by the update: config.php still holds production's database password" (with the file's own name). Put the file right, then on its Staging & Testing page press Look again (WordPress, Laravel) or Start the staging site (a custom app — see Set up by hand);
- otherwise it keeps running, its card says Running since before the last update — look at what was found, and what the panel found is folded under that line.
Safe by default
A copy holds your real customers' data, so it starts quiet:
| Switch | Off (the default) | On |
|---|---|---|
| Send email from the copy | every outgoing email is written to the copy's log instead (Laravel's log mailer — and a 6amMart copy's own mail settings in its admin say off; a WordPress copy writes each email to its WordPress debug log) | the copy mails exactly as production does |
| Run the queue worker and the scheduler (6amMart) | nothing runs by itself against copied orders, payouts or subscriptions | both run, as on production |
| Run the scheduled tasks | production's scheduled tasks are copied and switched off (WordPress's WP-cron too) | they run as on production |
Where the panel cannot switch email off. How an app sends mail is its own code. The panel switches it off for 6amMart, for WordPress and for a PHP app that takes its mail settings from a Laravel-style .env — Laravel itself, or an app whose .env already names its mailer (MAIL_MAILER, MAIL_DRIVER or MAILER_DSN). The files such an app reads over its .env are switched off with it — .env.local, .env.prod, .env.prod.local and the like, and the .env.local.php that composer dump-env writes — and if one of them sets the mailer in a way the panel cannot rewrite, the copy says its email is not switched off rather than promise it. A staging site of a custom app — Node.js, Create with AI, any other PHP app — keeps the mail settings it was copied with. The panel tells you three times: the Create a staging site dialog words the switch for the kind of website you are copying, the copy's job log says so in a line starting IMPORTANT, and the copy then carries an email not switched off mark beside its STAGING badge and a line in the banner on each of its pages. Change the mail settings on the copy before you test anything that sends email. On WordPress, a plugin that sends through its own service instead of WordPress's mail function is not covered either.
Always, whatever the switches: search engines are told not to index the copy (every response carries X-Robots-Tag: noindex, nofollow), and nothing on it is served from a cache, so what you test is what runs.
You can turn a scheduled task on later from the copy's Scheduled tasks page, and the queue worker from its Services page.
Payments, SMS and push notifications off (6amMart)
A shop's copy holds production's payment gateway keys and its customers' phone numbers, so Payments, SMS and push notifications off is ON when you make one. In the copy's own database — never production's — it:
- switches off every online payment gateway (Stripe, PayPal, Razorpay and the rest) and digital payment at checkout, so the copy cannot charge a card;
- switches off every SMS gateway (Twilio, Nexmo, 2Factor, MSG91…) and Firebase's login codes by SMS, so no customer gets a text from the copy;
- removes the Firebase push key, so the copy sends no push notification to anyone's phone.
Cash on delivery and the wallet stay as they are, so you can still place a test order. The gateways are switched off, not erased: their keys are still in the copy's admin. To test a card payment, enter that gateway's test-mode keys in the copy's admin and switch it on there — never production's live keys, and never production's Firebase file, which would reach your real customers' phones.
The create dialog lists exactly what it will switch off, read from production at that moment, and the job's log names each change. Anything the shop's database does not have (an older or a customised 6amMart) is skipped and named in the log, never an error. Refresh from production switches them off again after it copies production's database in.
You can turn it off for a copy — in the create dialog, or later in the copy's Copy settings (in its banner). The panel warns you first: the copy then keeps production's payment gateways, SMS gateways and push key, so a test order can charge a real card. Turned off later, it takes effect at the copy's next Refresh; turned back on, it is applied at once.
Two more things are always done on a 6amMart copy, and the create dialog lists them with the rest:
- Uploads kept on S3. The copy is switched to its own local storage, and the S3 key and secret in its database are replaced by a value S3 refuses — so the copy can write to and delete from production's bucket nothing. On the untouched 6amMart code, pictures that production keeps on S3 do not show on the copy.
- Google reCAPTCHA off, so you can sign in to the copy on its new address.
Its first .env also sets the queue to the database unless it is sync, database or redis, and broadcasting to the log unless it is reverb, log or null: the copy's jobs and events must not go where production's do. A queue worker of a copy is never started by another job (a certificate, Domain & SSL, a restore) while its switch is off. The keys that stay production's — maps, AI, social login — are listed in the dialog too.
A 6amMart copy is never activated. 6amMart registers its CodeCanyon licence to the address it is activated from, so activating the copy's address would move your licence away from production — whose admin panel would then lock itself within a day. The panel refuses it on a copy. 6amMart re-checks its licence about once a day and may then ask the copy for activation; refresh the copy from production instead of activating it.
Refresh from production
Refresh from production (on the copy's card, or in the banner on its pages) replaces the copy's database and files with production's as they are now. Anything you changed on the copy is lost. Its address never changes — a copy you gave your own domain stays on it, and the app is pointed at that address again — and its switches stay. Production is only read.
While it runs the copy is not started: its address says "This staging site is being refreshed." It is started again when the new files have been searched and the app has been asked — see What is asked again. A staging site of a custom app keeps the files that hold its own database password, and is started again when the search is clean; if the search finds production's password in the new files, it stays not started and its card lists them.
Such a copy of a Create with AI website is not refreshed while Build with AI is making a change on it — the panel asks you to wait for that change to finish — and while the refresh runs, Build with AI on the copy waits in turn: a new change, an Undo or Restore this version is answered with "this staging site is being refreshed from production" until it is over.
No staging site is refreshed while an AI Coding turn is working on it (or waiting for your answer): the panel says so — stop the turn, or let it finish, then press Refresh again. A refresh that was already waiting its turn when the coding turn began stops at its first step for the same reason, before anything of the copy is replaced.
Backups
The scheduled backups leave staging sites out: a copy is disposable, Refresh rebuilds it from production, and it would otherwise put a second full copy of your customers' data into every backup. The Backups page lists each copy under the schedule, so this is never silent, with an Include in scheduled backups switch for any copy you want kept — the same switch is in the create dialog and in the copy's Copy settings. A backup you run yourself (Run backup now) still takes every copy.
A copy that is left out is not in those backups, so it is not restored from them — after a restore, make a new copy.
Delete a copy
Delete staging site removes the copy's database, files, account, services, PHP pool, nginx configuration and certificate — the same delete as any project, with nothing kept back: the copies of its database that an import kept first, and its PHP slow log, go with it. Production's data and code are not touched. A 6amMart staging site was one more project on the server while it existed, so each shop had a smaller share of the PHP workers and of the cache; the delete ends by giving those back — no restart is needed (see More than one project, "Sizing the server"). What happens to its address:
- A temporary address is deleted at AllsWeb, with its DNS records and their history. If AllsWeb cannot be reached at that moment, the panel keeps the request and tries again when it starts and every hour until AllsWeb confirms — a temporary address is never left pointing at this server. A copy removed some other way (a website's own Delete, a shop deleted from Projects) has its address released within the hour too.
- Your own domain: its Cloudflare record is removed — only a record that still points at this server, whether the panel made it or you did; one you have since pointed elsewhere is left alone, and so is a record in an account the panel was not given. A record at another DNS provider is yours to remove. A copy that has both a temporary address and your own domain gets both: the dialog says so before you confirm.
Working on a branch and promoting
When production's code comes from a git repository — a website connected to git, or a 6amMart project whose admin app (and customer website) were installed from git — a test copy can work on a branch of its own, and Promote moves what you tested there to production.
Moving tested changes to production needs git. A project deployed by upload or from files can have a staging site and everything else on this page; it has no Promote, and its copy's page says so.
Make the copy on a branch
The create dialog has Work on its own branch, ticked. Choose:
- A new branch —
stagingis offered (orstaging-<the copy's short name>when the repository already has astaging). The panel makes it from the commit production runs right now, so the copy's files and its branch agree from the first minute. - A branch the repository already has — the copy is made from production and then deployed from that branch.
The copy uses production's own way of signing in to the repository — its deploy key or its access token. There is nothing new to add at your git host.
Making a new branch means writing to the repository, and the panel asks your git host first, with a push that moves nothing. If the answer is no — a deploy key added without Allow write access, a token that may only read — the dialog says so before anything is made, with a link to the repository's settings. Two ways on: give the key or token write access, or make the branch at the git host yourself and pick it as an existing one. (With a read-only key the copy still deploys from its branch; Commit and push and the panel's own merge are what need write access.)
The copy follows its branch
- Deploy the copy (on its git card, or Update on the copy's own Deploys page) pulls the branch.
- A push to the branch deploys the copy by itself when production has automatic deploys on: one webhook serves production and its copies. A push to production's branch deploys production as always; a push to the copy's branch deploys the copy; a push to any other branch is ignored. There is no second webhook to add.
Changes made on the server
If you — or a coding agent — changed files on the copy (Files, SSH), the git card lists them: changed, new, deleted, against the commit the copy runs. Commit and push shows the list once more, asks for a message, and pushes one commit to the copy's branch. Never to production's branch, and never a forced push; if the git host refuses it, you read what it answered.
After Commit and push, deploy the copy once. The commit puts your change in git, and the copy's files are already that commit — but the copy has not deployed it: its build, its deploy commands and its database migrations have not run for it. While the card says committed on the server, not deployed on the copy yet, press Deploy the copy, test, and then promote. Promote waits for that deploy, so a migration never runs on production before it has run on the copy.
The same holds for a commit that reached the branch another way — a push from your own computer, production's commits brought in — when the copy's deploy of it stopped part-way (a migration that failed, say). The card then says its last deploy there did not finish: fix what failed, press Deploy the copy until it finishes, test, and then promote.
While a website's copy holds changes that are not in git yet, Deploy the copy on the card is refused, and a push to its branch deploys nothing (the webhook's delivery says why): the deploy would put the branch's files over them. Commit and push first. (Update on the copy's own Deploys page stays the ordinary deploy: it replaces the files with the branch's.)
A deploy that stopped half-way is not such a change. A PHP website's deploy puts the new files in place before it installs packages and runs your deploy commands; when one of those fails, the copy still runs the commit it ran, and the files that deploy brought are not listed as changed. Put right what failed and press Deploy the copy again, or push the fix. A file you edit on the server after that is listed as always, and Commit and push commits it on top of the commit that deploy brought.
Left out on purpose, because they are the server's and not your code: every .env file, node_modules/, vendor/, build folders (.next/ and the like), upload and cache folders at the top of the project (storage/, uploads/, cache/, public/uploads/, wp-content/uploads/), log files, a package-lock.json or composer.lock the install wrote because your repository has none (one your repository has is listed when it changed), a coding agent's own files at the top of the code when your repository does not have them (.claude/, .codex/, .cursor/, .mcp.json, CLAUDE.md, AGENTS.md), anything your .gitignore leaves out, and what the website's settings keep between deploys. On a 6amMart copy also what the app itself owns: the activation result, the module switches and translations typed in the admin panel. And two things of the copy itself, found by what they hold: every file that holds the staging site's own database password, and what arrived from production when the copy was made or refreshed (see The staging site's own database settings never travel).
On a website's copy two more things are never listed and never committed. A folder that is a git repository of its own (somebody ran git clone or git init on the server): make it part of your project in your own repository if you want it there. And files your repository marks export-ignore in .gitattributes: a deploy never puts them on the server, so their absence is not a deletion and the commit leaves them in the branch.
A WordPress website's theme or plugin is changed in its repository, not on the server: push to the branch and the copy deploys it.
Production moved on
The card says how many commits the branch is ahead of production's branch and how many it is behind. Bring production's commits in merges production's branch into the copy's, pushes, and deploys the copy — so what you test includes what production got meanwhile. If both changed the same lines, nothing is merged or pushed and the files are named: settle them on your own computer, push, and the copy deploys the result.
Promote
Promote to production is offered when all five are true, and the card says which is not:
- the branch is ahead of production's — there is something to move;
- it is not behind — production's newer commits were tested with it;
- nothing changed on the server is uncommitted;
- the copy runs the branch's newest commit;
- the copy has deployed it — after Commit and push, or after a deploy of the copy that stopped part-way, that is one press of Deploy the copy that finishes.
Together they mean production receives exactly the files the copy ran, after the copy's own deploy ran for them — with one exception that is on purpose: a file that holds the staging site's own database settings stays on the copy, and production gets the repository's version of it. You type production's short name, and one job runs on the production project:
- checks all five again;
- backs up production's database — a dump kept beside the project; the newest two stay;
- merges the branch into production's branch (a merge commit, with your message) and pushes it;
- deploys production the ordinary way: pull, build, database migrations, caches;
- checks that production answers.
Files changed by hand on production that are not in git are looked for before the merge. The dialog lists them, and the promote is refused until you choose: tick throw these edits on production away — the repository's version then replaces them — or make the same change on the staging site, commit it there, and promote again. A connected assistant can press Promote for you only with that list empty: the tick is yours alone.
If anything after the backup fails, the panel puts production back: its database from that dump — when a migration could have touched it — and its code to what it ran. The job's last line then says the one thing it cannot undo: the merge is still on production's branch at your git host. Fix the problem on the copy's branch and promote again, or revert that merge at the git host; until then, an Update of production would deploy it.
Putting the database back returns it to the moment of the backup: what visitors did in the minutes between is lost with it. That is why it happens only when the deploy got as far as changing the database.
Open a pull request instead
Open a pull request on the card opens your git host's compare page for the two branches (GitHub, GitLab, Bitbucket). Review and merge there. Once production's branch holds what the copy's branch holds, the card says Merged at the forge and the button becomes Deploy production: the same job without the merge — backup, deploy, check, and put back on failure. This also works with a read-only key.
Database changes travel as files
A database change reaches production the way code does: as a migration file in the repository, run by the deploy — on the copy first, on production at promote.
- 6amMart: every update runs
artisan migrate --force. - Laravel app: its deploy commands (Settings), for example
php artisan migrate --force.
Nothing copies the copy's database to production. Rows you added while testing stay on the copy — and so does a table or a column you changed by hand there: see Database changes made by hand.
Refresh and delete, on a branch
Refresh from production brings production's database and files again and then puts the branch's code back on top, running its migrations — a rehearsal of the promote on fresh data. It is refused while the copy holds changes that are in no commit, unless you tick throw the copy's changes away. (A 6amMart copy only moves forward: when the branch does not contain what production now runs, the refresh leaves production's code and tells you to press Bring production's commits in.)
Delete staging site offers also delete the branch when the panel made it. A branch you made yourself is never deleted.
Without git: taking a tested change to production
A project that is not deployed from a git repository has no Promote. Its copy's card says how a change you tested reaches production, for its kind:
- 6amMart — Tried an update here? Run the SAME zip on production: its Deploys page → Update from a zip. It keeps
.envand the uploads and runs the database migrations. - A static website or a Laravel app — upload the same ZIP on production's Deploys page. Never a ZIP made from this staging site's folder: it holds the staging site's database settings, and production refuses it. An upload is a whole version: a file that is not in the ZIP is removed.
- WordPress — deploy the same ZIP of your theme or plugin on production's Deploys page. An update, or a setting changed inside WordPress itself, is made again on production.
- Create with AI (a staging site made on an earlier version) — ask Tia for the same change on production's Build with AI page.
Database changes made by hand
A change you made by hand in the staging database — a new table, a changed column — does not travel. Promote carries files, and nothing compares the two databases for you while it runs. The by-hand road:
- On the staging site's Database page press Compare with production. It reads both databases' structure once, changes nothing, and shows three lists — "only here", "only on production", "different definition" — for tables, columns and indexes, and the line "not compared: rows, views, triggers, routines, foreign keys".
- Write the change as SQL yourself (
ALTER TABLE …). The panel writes no SQL for you and runs none. - Run it on production's Database page with Import. Every import takes a copy of the database first: the panel checks there is room for it, keeps the newest two in a folder of their own, and its answer names the file. With no room the import is refused and nothing is changed.
There is no "structure only" export, on purpose: a file made the way the panel dumps a database drops every table it names when it is imported. And never export a staging site's whole database and import it on production — that puts test rows over real ones.
What a staging site cannot promise
The limits, said plainly:
- An app that hides its database password — encrypted, split in two, or fetched from somewhere else — cannot be seen by any search. That is why the panel never starts a custom app's staging site by itself — the Staging & Testing page offers none, and one AI Coding sets up waits for your press — and why the four kinds it does start are asked: the search is the second check, never the only one.
- A promote that fails after a migration ran puts production's database back to the copy taken first: what visitors wrote in those minutes is lost with it.
- A Laravel app's own payment, SMS and storage keys stay as they were copied: the panel switches its email off and asks about its database, queue, cache and disk, and does not know what else your code calls. Change them on the copy before you test anything that uses them. And a static site's pages may call production's API from the staging address.
- phpMyAdmin and root's own shell are by hand. What you import through phpMyAdmin, or start as root with
systemctl, the panel does not see. - If you point a staging site at production by hand — its password, or its database name, typed into a file — nothing stops you at the keyboard. The next deploy, refresh or Look again finds it and stops the staging site; until then it runs with what you typed. Press Look again after you change such a file.
Good to know
- A copy cannot be copied: make another copy of the original instead.
- If you delete a shop that has staging sites, the staging sites stay, each as a project of its own: it can still be updated, looked at and deleted, but no longer refreshed — there is nothing left to copy from. Its files are still searched for the deleted shop's database password for as long as that shop can be put back. One case has no way on: a customer website that was not started (its build named production's address) stays not started once the shop's data has been removed for good — delete that staging site when you no longer need it.
- A copy stays on the server it was made on, beside the project it is a copy of — Move project refuses it. Make a new copy on the other server.
- A temporary address is DNS-only (not through Cloudflare) and its certificate is Let's Encrypt's, issued over HTTP — port 80 must be reachable from the internet. For the same reason it still answers when Refuse visitors that skip Cloudflare is on: it never goes through Cloudflare. Its DNS record is AllsWeb's, so the panel never changes it — even with a Cloudflare token stored — it is not listed on the Cloudflare tab, and the check-up does not count it as one of your addresses.
- Your own domain on a copy is treated as any domain of yours, name by name: its record is proxied, it is on the Cloudflare tab and in the check-up, and with Refuse visitors that skip Cloudflare on it must be behind Cloudflare like every other address — a copy that has both kinds of address answers directly on the temporary one only.
- If this server's public IP address changes, the copies' temporary addresses are moved to the new one automatically.
- Each AllsWeb account can hold a limited number of temporary addresses at once; delete copies you no longer need.
- The copy's short name works everywhere a project's does — its own pages, the
sixpanelcommand, a backup you start by hand.
Something unclear?Ask Tia