Connecting a second panel
A second brand is a second CoreCP installation. It runs the same software and installs the same packages from the same source — and shares nothing else. Its own database, its own customers, its own keys, its own certificate authority, its o
Written for: Administrator
A second brand is a second CoreCP installation. It runs the same software and installs the same packages from the same source — and shares nothing else. Its own database, its own customers, its own keys, its own certificate authority, its own branding.
What the first panel (the primary) does know about the second (the replica): which version it runs, whether it is healthy, which release channel and wave it is in, and whether its licence is live. Nothing else. That is not a rule somebody has to remember: the panel at the other end simply has no command that could return a customer's anything.
Administrators only. Resellers and end customers never see any of this.
What you need
- A second server. Bare is fine: a clean Ubuntu 26.04 with root access is enough — the one-liner below makes that machine install CoreCP itself. A machine that already runs a panel works too; then you only connect.
- A hostname for that machine that resolves publicly. Without a DNS record it gets no Let's Encrypt certificate and serves a self-signed one until you publish the record.
- Outbound HTTPS from that machine to port 8443 on this panel, and to
packages.corecp.dev. Nothing needs to be open inbound: it dials out, never the other way round. - About 10 GB of free disk; the install checks and says so if it is tight.
Two worlds, and the line that bridges them
Installing and connecting are still two different jobs in two different places — the same split Plesk 360 and Rancher make. What is new is a line that does both in one go:
| Where | Why | ||
|---|---|---|---|
| Installing a CoreCP panel on the second machine | at a terminal on that machine, as root (`curl -fsSL https://get.corecp.dev \ | CORECP_PANEL=1 bash`) | it writes systemd units, creates a database and mints that machine's own certificate authority |
| Connecting it to this one | in the panel, under Fleet → Managed panels | it is a guided, infrequent setup that benefits from a form, a validation and a confirmation | |
| Both at once, in one pasted line | the wizard prints the line; you paste it in that machine's terminal | the code carries everything the machine needs — where it belongs, which channel, where to report its progress |
The third row changes nothing about the first two. This panel still never installs anything on the other machine and never holds a login for it: it hands out a line to paste, and whoever has root over there pastes it. Everything after that dials from that machine to this panel — never the other way round.
The short way: in the panel
Fleet → Managed panels → Connect a panel. Administrators only — a reseller does not see the entry, and the API refuses the routes behind it whatever the sidebar says.
- Give the panel a short identifier (
panel2), a name for the list, and the channel and wave it should update in. - Answer "What is on the other machine?":
- Nothing yet — a clean Ubuntu server: you get the line that installs and connects.
- A CoreCP panel is already running: you get the line that only connects. You are shown both either way; the choice only puts the right one first (the other sits under The other line). The same code works for both.
- Optionally pin it to an IP address — the outbound address of the other machine. A connect from anywhere else is then refused.
- Press Issue the code. The panel shows the exact line, with a copy button and its expiry counting down:
curl -fsSL https://get.corecp.dev | CORECP_PANEL=1 CORECP_CONNECT_CODE=corecpf1.eyJ2IjoxLCJwIjoicGFuZWwyIiwi… bashor, on a machine that already runs a panel:
corecp-panel connect --code corecpf1.eyJ2IjoxLCJwIjoicGFuZWwyIiwi…The code is shown once. Only a hash of it is stored, so this is the only place it will ever exist. Lost it? Issue a new one for the same panel — nothing else has to be redone. Withdraw the code kills it immediately.
- Paste the line on the other machine, as root. Then watch the screen: the progress card fills in as it happens, phase by phase.
The progress card
Six phases, in this order. That machine reports them itself, outbound — this panel is not looking over its shoulder and could not.
| Phase | What happens |
|---|---|
| Checking the machine | is this root, is it Ubuntu 26.04, does the machine have a hostname, is there disk, is this panel reachable |
| Adding the package source | our own apt source and the key everything is signed with |
| Installing CoreCP | apt-get install corecp-panel — the binary, the web interface, the units |
| Database, units and its own CA | PostgreSQL, the service account, the systemd units, the nginx vhost, and the node CA minted on that machine |
| Connecting to this panel | the code is redeemed; the two node CAs are compared and must differ |
| Running and answering | the service is up and the panel reports in |
If something goes wrong the card stops on the phase where it went wrong, with the error and an expandable log below it. Issue a new code and try again; nothing has to be cleaned up first.
A successful connect then shows the version, the health and the fingerprint of the replica's own node CA.
Afterwards the list shows every connected panel with its version, health, channel, wave and licence. Clicking a row opens it: assign a channel, assign a wave, trigger an update, hold updates, and — behind typing the panel's name — withdraw its licence.
What the screen will never do. It shows the fleet CA's fingerprint next to this panel's own node CA, so you can see with your eyes that they are two different authorities. It does not create, rotate or export either of them: those are root operations at a terminal (corecp-panel panels ca). And there is no button anywhere on it that reaches the other panel's customers — the agent at the other end implements no instruction that could answer.
The long way: at a terminal
Everything the screen does is also a command, which is what you want for ten panels rather than one. The rest of this page is that path.
Step 1 — create the panel on the primary
$ corecp-panel panels add panel2 --name "Second brand" --channel beta --wave 2 \
--config /etc/corecp-panel/panel.yaml
managed panel panel2 created (beta, wave 2)
Next: corecp-panel panels code panel2 --config /etc/corecp-panel/panel.yamlpanel2 is a short slug you choose. It becomes the common name of the panel's certificate and the login half of its apt credential, so keep it short and leave it alone afterwards.
--channel is the release channel: edge or beta. stable and steady are accepted too, but on the build server's repository they are aliases of beta and serve exactly what beta serves. --wave is which wave this panel updates in. Panels are the outermost ring of the same wave system the servers use.
Step 2 — ask for a connect code
$ corecp-panel panels code panel2 --ip 185.117.226.123 --ttl 15m \
--config /etc/corecp-panel/panel.yaml
Run this on panel2, within 15m0s — on a machine with nothing on it yet:
curl -fsSL https://get.corecp.dev | CORECP_PANEL=1 CORECP_CONNECT_CODE=corecpf1.eyJ2IjoxLCJwIjoicGFuZWwyIiwi… bash
or, on a machine that already runs corecp-panel:
corecp-panel connect --code corecpf1.eyJ2IjoxLCJwIjoicGFuZWwyIiwi…
progress https://panel1.corecp.dev/managed-panels/panel2
run id 0f2b1c4e-8a77-4a11-9a1e-9b0a2c7f5d31
valid until 2026-08-09T17:10:24Z
pinned to 185.117.226.123 — a connect from any other address is refused
The code is single use and is burned on the first attempt that gets past the address check.Pass --installed when the target already runs a panel: the connect line then comes first, and the progress card knows not to expect install phases.
Use --ip. Without it the code works from anywhere; with it the code works only from the machine you meant. A code somebody reads over your shoulder is then worthless.
The code is single use and expires on its own. There is no password inside it: the apt credential comes back in the reply, over a channel the replica has already verified. A code in a chat window is therefore not a password in a chat window.
Step 3 — paste the line on the other machine
On a bare machine
One line, as root. It installs and connects in one go, and reports every phase back to this panel as it goes:
# curl -fsSL https://get.corecp.dev | CORECP_PANEL=1 CORECP_CONNECT_CODE=corecpf1.eyJ2… bash
[corecp] installing base packages
[corecp] == preflight == checking this machine before anything is installed
ok running as root
ok operating system
ok this machine has a domain name
ok free disk space
ok the primary panel is reachable
[corecp] == repository == adding the CoreCP package source and its key
ok signed apt source
[corecp] == install == installing corecp-panel and everything it runs on
== runtime packages
== service account and directories
== node certificate authority (this machine's own)
[corecp] CA ready in /var/lib/corecp/ca
== PostgreSQL
created database corecp_panel
== configuration
wrote /etc/corecp-panel/panel.yaml
== database migrations
== systemd units
== nginx vhost
ok corecp-panel installed
[corecp] == connect == redeeming the code against the primary
connected as panel2
[corecp] == health == starting the panel and checking that it answers
ok corecp-panel is running
ok it reports to the primary
[corecp] done. This machine is a panel and it is connected.
[corecp] its own address https://panel2.corecp.dev
[corecp] create an admin corecp-panel user add --email <you> --level admin --config /etc/corecp-panel/panel.yamlThat last line is not a detail: a fresh panel has no administrator yet. Make one before you open the browser.
To install now and connect later, leave the code out:
# curl -fsSL https://get.corecp.dev | CORECP_PANEL=1 bashOn a machine that already runs a panel
$ corecp-panel connect --code corecpf1.eyJ2IjoxLCJwIjoicGFuZWwyIiwi… \
--config /etc/corecp-panel/panel.yaml
connected as panel2
channel beta (wave 2)
fleet CA 739d876dc5d26c76
its node CA c4430ea564db75b6
our node CA ed466a89fea08858 (verified different — neither panel signs for the other's nodes)
apt login panel-panel2 at packages.corecp.dev
Restart the panel to start reporting: systemctl restart corecp-panelThat fourth line is the most important one on this page. The two panels showed each other their own node CA and established that there are two of them. Had they matched, no certificate would have been issued and nothing would work — which is correct.
Then:
$ systemctl restart corecp-panel
$ corecp-panel connect status --config /etc/corecp-panel/panel.yaml
connected to panel1.corecp.dev:8443 as panel2
since 2026-08-09T16:45:23Z
channel beta (wave 2)
entitlement installed
last contact 9s agoIt can take up to a minute before apt works on the replica: the build server pulls the list of valid credentials from the primary once a minute.
Step 4 — look from the primary
$ corecp-panel panels list --config /etc/corecp-panel/panel.yaml
ID STATE CHANNEL WAVE HOLD VERSION HEALTH LICENCE LAST SEEN
panel2 connected beta 2 no 0.14.0 ok live 9s agoWhat you can do with a connected panel
$ corecp-panel panels channel panel2 stable --config … # a different channel
$ corecp-panel panels wave panel2 1 --config … # a different wave
$ corecp-panel panels upgrade panel2 --config … # update now
$ corecp-panel panels hold panel2 --config … # no updates for now
$ corecp-panel panels resume panel2 --config … # updates again
$ corecp-panel panels revoke panel2 --config … # withdraw the licence
$ corecp-panel panels reinstate panel2 --config … # give it backEvery one of those is also a control in Fleet → Managed panels, on the same API — a fleet of forty panels stays scriptable, and one panel stays a click.
An instruction is queued and carried out the next time the replica reports (every 15 to 30 seconds). What actually happened comes back — in the terminal, and in the Outstanding instructions list on the panel's own screen:
$ corecp-panel panels show panel2 --config … | tail -3
DIRECTIVE VERB STATE DETAIL
1 update.apply done upgraded: corecp-agent 0.30.12 -> corecp-agent 0.32.7And that is the whole list. There is no panels accounts, no panels sql, no panels shell — not because they were forgotten, but because the agent at the other end has no answer for one.
How a managed panel installs, and what it refuses
A replica does not take a package because apt offered it. Before dpkg is allowed near a file, the panel checks the same three things a hosting server checks:
- the signed release manifest for the exact version apt intends to install verifies under a key that is compiled into the software, not one that lives on the repository server;
- the version in that signature is the version being installed;
- the file apt downloaded is the exact length and the exact checksum the manifest names.
If any of the three does not hold, nothing is installed and the instruction comes back with the sentence that says which one:
$ corecp-panel panels show panel2 --config … | tail -2
DIRECTIVE VERB STATE DETAIL
7 update.apply failed refusing the update: corecp-agent_0.32.7_amd64.deb does not
match its signed SHA-512 — the file apt fetched is not the
file we publishedThat is not a broken download. apt verifies the repository; it does not verify the repository server, and this is the check that does.
A pinned version is about one package. corecp-panel and corecp-agent carry the same product version, but a pin still moves one of them: a bare number is read as the panel's own version, which is the number the list shows. On a machine that carries both and where you mean the agent, name it:
corecp-panel panels upgrade panel2 --version corecp-agent=0.50.0 --config …A number the panel cannot place is refused rather than guessed at.
Going back to an older version
A panel remembers the highest version it has ever accepted, and refuses anything below it. A signed but older package is how a fleet gets walked back onto a hole that was already fixed, so "older" is a refusal by default:
$ corecp-panel panels upgrade panel2 --version 0.30.12 --config …
$ corecp-panel panels show panel2 --config … | tail -2
DIRECTIVE VERB STATE DETAIL
8 update.apply failed refusing the update: corecp-agent 0.30.12 is older than
0.32.7, the highest version this node has acceptedThere is an honest way past it, and it asks for two things: the step back by name, and a sentence saying why.
corecp-panel panels upgrade panel2 --version 0.30.12 \
--allow-downgrade --downgrade-reason "0.32.7 breaks the mail queue on this panel" \
--config /etc/corecp-panel/panel.yamlIn Fleet → Managed panels it is the same two things: pin a version, tick This is a deliberate step back, and the button stays shut until the box below it has an answer.
The reason is not paperwork. It is written to this panel's audit log and to the replica's own update ledger, where it stays:
$ corecp-panel panels show panel2 --config … | tail -2
DIRECTIVE VERB STATE DETAIL
9 update.apply done DOWNGRADED by primary directive 9: corecp-agent 0.32.7 ->
corecp-agent 0.30.12 (reason: 0.32.7 breaks the mail queue…)The floor itself does not move down. The next ordinary update goes forward again with no override at all.
What revoking does, and what it does not
panels revoke removes the apt credential and closes fleet access. Within a minute that panel can install nothing.
It touches no data. Its customers keep being served, the database stays, the certificates keep renewing, the backups keep running. A licence ending is a panel that stops receiving updates — not a panel that stops.
Disconnecting again
On the replica:
$ corecp-panel connect leave --config /etc/corecp-panel/panel.yaml
left the fleet of panel1.corecp.dev:8443 (was panel2)
A running panel notices within one poll interval and stops reporting; there is
nothing to restart.The apt credential stays until you remove it yourself (rm -f /etc/apt/auth.conf.d/corecp-fleet.conf).
Why its own CA
Each panel has its own certificate authority for its servers. You must never share one between two brands. If they shared one, a replica that was taken over could mint certificates the primary's servers trust — and then the second brand is a key to the first.
So there are three: the primary's node CA, the replica's node CA, and a third authority for the link itself (the fleet CA). Both sides check this when they connect and refuse if any two of them are the same.
$ corecp-panel panels planes --config /etc/corecp-panel/panel.yaml | head -8
PLANE SHARED THING WHY
build SHARED apt repository one pool of packages: …
…
data per panel node CA NEVER shared: a compromised replica …See also: docs/architecture.md, "Primary and replica panels (three planes)", and docs/research/ui-vs-cli.md for why connecting is a screen and key custody is not.