Portalis / Documentation / v1.0.27
Portalis documentation
Everything below is written against the shipping product and shot on a running instance. Where something is not built yet, it says so rather than describing what it ought to do.
What Portalis is
Portalis is a self-hosted SSH gateway. You install it on one machine, add the servers your team works on, and from then on people reach those servers through Portalis instead of holding the credentials themselves. Every session runs through the gateway, so every session is recorded, every command can be checked against a rule, and access can be handed out and taken back in one place.
Two doors lead in, and both end up in the same audit log:
- The browser. A full terminal, a file manager and a database browser in the web interface. Nothing to install on the laptop.
- An ordinary SSH client.
ssh you@your-gateway -p 2222gives a menu of the servers you may reach. Your own key, your own terminal, your own tooling.
Portalis holds the credentials for the servers. The people using it never see them, and when someone leaves you switch off one account rather than rotating a password on forty machines.
What you need
| A machine for Portalis | Any 64-bit Linux host with Docker and Compose v2. A small VPS is plenty: 1 vCPU and 1 GB of RAM run it comfortably, 2 GB if you keep a lot of recordings. The installer offers to install Docker if it is missing. |
|---|---|
| Two ports | 8090 for the web interface and 2222 for the SSH gateway. Both can be changed at install time. |
| Reachability | Portalis has to reach your servers on their SSH port. Your servers do not need to reach Portalis, and they need nothing installed on them. |
| A name and a certificate | Not required to start, but put Portalis behind a reverse proxy with HTTPS before real people use it. Caddy, Nginx, Traefik and Cloudflare Tunnel all work. |
| A licence | The Free plan covers five servers and needs no key. Pro and Enterprise come with a key you paste in once. |
Portalis is not an agent. Nothing is installed on the servers you connect to. It signs in over SSH the way you would, with a key it keeps to itself.
Install it
One line on the machine that will run Portalis:
curl -fsSL https://get.portalis.sh | sudo bash
The installer does four things and tells you which it is on:
- Checks prerequisites. Docker and Compose v2 must be there. If they are not, it asks whether to install Docker from
get.docker.com; answer n and it stops without touching anything. - Prepares
/opt/portalis. A compose file, a generated.envwith fresh secrets, and systemd units for the update checker. Re-running the installer on an existing install keeps your.envand your data. - Pulls the image and starts the containers. Portalis itself, PostgreSQL for its own data, and Redis.
- Waits until it answers. Then it prints the URL and the one-time bootstrap password.
Write that password down before you close the terminal. It is shown once and it only works for the first sign-in.
Choosing ports, a version or a directory
Every knob is an environment variable in front of the command:
# a different web port, and pin the version
curl -fsSL https://get.portalis.sh | PORTALIS_PORT=9000 PORTALIS_VERSION=1.0.27 sudo -E bash
| Variable | Default | What it does |
|---|---|---|
PORTALIS_PORT | 8090 | Web interface port |
PORTALIS_SSH_PORT | 2222 | SSH gateway port |
PORTALIS_BIND | 0.0.0.0 | Address to bind to; set 127.0.0.1 when a reverse proxy sits in front |
PORTALIS_DIR | /opt/portalis | Where the install lives |
PORTALIS_VERSION | latest | Pin a version instead of following the newest |
PORTALIS_INSTANCE_NAME | the host's name | How this install is labelled in your account portal |
PORTALIS_INSTALL_DOCKER | ask | 1 installs Docker without asking, for unattended runs |
The installer writes everything it did to /var/log/portalis-install.log, with the bootstrap password masked.
Behind a reverse proxy
Bind Portalis to localhost and let the proxy hold the certificate. A Caddy example, which is the whole file:
portalis.example.com {
reverse_proxy 127.0.0.1:8090
}
Then tell Portalis to trust that proxy, so the audit log records the visitor's address instead of the proxy's. In /opt/portalis/.env:
PORTALIS_TRUST_PROXY=1
PORTALIS_TRUSTED_PROXIES=127.0.0.1
The SSH gateway on port 2222 is a raw TCP listener, not HTTP. It cannot go through an HTTP reverse proxy — expose the port itself, or put a TCP proxy in front of it.
First login
Open the URL the installer printed and sign in as superadmin with the bootstrap password. Portalis then makes you do two things before it lets you anywhere:
- Set up two-factor authentication. Scan the code with any authenticator app. This is not optional: an SSH gateway with a password-only administrator is a bad trade.
- Replace the bootstrap password. The one from the installer stops working the moment you do.
You are also handed a set of recovery codes. Each one works once, in place of an authenticator code. Put them where you keep your other break-glass credentials.
Your licence
A fresh install runs on the Free plan: five servers, the browser terminal, the SSH gateway, recordings and the audit log. No key needed, nothing expires.
Pro and Enterprise are bought at my.portalis.sh and lift the server limit and switch on the paid modules. After paying you get a licence key by email. Paste it under Settings → License & Modules and it applies straight away — no restart.
Changing plan later needs no copy-and-paste. When you upgrade or downgrade in the portal, the key is re-signed for the same licence and your instance picks it up at its next check-in, then logs license.updated_from_portal. Nothing to do on the server.
| Free | Pro | Enterprise | |
|---|---|---|---|
| Servers | 5 | Unlimited | Unlimited |
| Browser terminal, SSH gateway, recordings, audit log | Yes | Yes | Yes |
| File manager, command guard, just-in-time access | — | Yes | Yes |
| Database browser | — | — | Yes |
| Single sign-on | — | — | Yes |
Your instance checks in with the licence server once a day. If it cannot reach it, paid features keep working for a week of grace before they fall back to Free. An instance that never reaches the internet can run air-gapped — ask us for a key issued that way.
Add a server
A server in Portalis is one machine it can sign in to. Go to Servers → Add server.
| Name | How the server appears in lists and, more importantly, in the audit log. Use the name your team already says out loud. |
|---|---|
| Host or IP address | Whatever Portalis itself can resolve and reach. A private address is fine and usually better. |
| Type | Linux (SSH) for ordinary servers. The panel types add a link to that panel's own interface; the SSH side is identical. |
| Group | The server group it belongs to. Groups are how access is handed out, so this field is worth getting right — but you can move a server later. |
| SSH username | The account Portalis signs in as: root, deploy, whatever you use. Everyone who opens a session on this server lands in that account, under their own name in the log. |
| SSH port | 22 unless you moved it. |
| How Portalis signs in | With its own key (recommended) or with a password you store once. See below. |
Press Test the connection first. A server that Portalis cannot reach would sit in the list doing nothing, so the save button stays disabled until the test comes back green.
The gateway key
Portalis generates one SSH key pair for itself at install time and presents it to every server. That key is what makes the whole thing work without storing passwords: you install the public half once per server, and Portalis never needs a credential from you again.
Copy it from Settings → Security and add it to the target account's authorized_keys:
# on the server you are adding, as the account Portalis will use
mkdir -p ~/.ssh && chmod 700 ~/.ssh
echo 'ssh-ed25519 AAAA... ezssh-gateway' >> ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys
If installing the key by hand on forty servers is not appealing, add the server with a password once instead. Portalis signs in, installs its own key, and from then on uses the key. The password is not kept afterwards.
The gateway key does not rotate on its own and does not change when you update Portalis. Install it once per server and forget about it. It is shown in full under Settings → Security whenever you need it again.
Host keys
The first time Portalis connects to a server it records that server's host key and pins it. If the key changes later — a rebuild, a restore, someone in the middle — the connection is refused and the audit log says why. After a legitimate rebuild, clear the pin on the server's row and let the next connection pin the new one.
Server groups
A server group is a bag of servers with a name and a colour: Production, Staging, Databases, Customer: Northwind. They exist for two reasons: the lists stay readable, and access is granted per group rather than per machine.
That last part is the point. Grant the DevOps team the Production group today, add web-03 to that group next month, and the whole team can reach it without anyone touching permissions again.
Group by what decides access, not by what a server runs. If Support may reach staging but not production, then Staging and Production are the right groups. If a customer's servers are handled by one team, a group per customer beats a group per role.
cPanel and other panels
Setting a server's type to cPanel, WHM, DirectAdmin, Plesk or CyberPanel changes two things: the row gets that panel's badge, and you can store the panel URL so people reach the control panel from the same list. Everything else — SSH, files, recording, the command guard — behaves exactly as it does for a Linux server.
Database connections
The database browser is an Enterprise module and it is worth being precise about what it connects to, because it is the one part of Portalis that does not go through your servers.
A database connection is its own thing: a MySQL or PostgreSQL login that Portalis dials directly. It does not use your server list, and it does not tunnel through SSH. The database has to be reachable from the machine Portalis runs on.
Go to Database → New connection and fill in host, port, database name, user and password. Group it the way you group servers — Reporting, Production data — because database groups are how access is granted.
Test connection works the same way as it does for servers. Editing a connection later leaves the password field blank: fill it in only when you want to change it.
If the database only listens on 127.0.0.1 on its own machine, Portalis cannot reach it. Either bind it to an address Portalis can reach and firewall it accordingly, or keep using the SSH terminal and the database's own client on the server. Tunnelling a database connection through the gateway is on the roadmap and not built yet.
How access works
Four things decide whether a person can open a session on a server. Read them in order — it is the same order Portalis uses.
| Layer | What it grants | Where you set it |
|---|---|---|
| Role | Super admin and admin reach every server. Everything below applies to ordinary users. | Users |
| Team | A user group is granted whole server groups. Every member inherits every server in them, including the ones added later. | Groups → User groups |
| Person, whole group | One person can be granted a server group directly, without being in a team. | Groups → User access |
| Person, one server | One person can be granted a single server on top of everything else. | Groups → User access |
They add up: someone in two teams reaches the union of both. One rule overrides the rest — a deny on a specific server wins, whatever granted it. That is how you hand a team the whole of Production and still keep one machine out of their reach.
The recommended order when setting up is: server groups first, then servers, then teams, then people. Do it that way and every account you create is useful the moment it exists.
Add a user
Users → Add user. Username, full name, email, role, and a one-time password you either type or generate.
| Role | Reaches |
|---|---|
| User | Only what their teams and personal grants give them. This is almost everybody. |
| Admin | Every server, plus servers, users, groups, the guard and the audit log. |
| Super admin | Everything an admin does, plus the licence, updates and other super admins. |
The password you set is one-time: the person must replace it at their first sign-in, and they have to enrol two-factor authentication before they reach anything. You can also paste their SSH public key while creating the account, which saves them a step — and after the account exists, the same dialog lists the keys on it so you can check or remove one.
Deactivating a user is usually better than deleting them: their history stays readable in the audit log, and their sessions stop immediately either way.
Teams and their servers
A user group is a team. Give the team whole server groups and everyone in it inherits them.
- Open Groups → User groups and press New user group. Name it after the team, not the access: DevOps, Support, Northwind team.
- Open the group and tick the people in it. The chips fill in as you go.
- Under Server groups this team reaches, tick the groups they need. That is the grant — there is no save button, it applies as you click.
This is the layer you want to be using for almost everything. A new server joins a group and the right people can already reach it. A new colleague joins a team and has the right access before their first day is over. Nothing to review server by server.
One server for one person
Sometimes a team grant is too much. A support engineer needs to look at one database server, once, and putting them in Database admins would hand them all of it. Portalis has a layer for exactly that.
- Go to Groups → User access.
- Find the person and open their row. Everyone is listed with how many single servers they already hold.
- Open the group the server sits in — the groups here are only a way to find the machine, not a grant.
- Tick the one server. It applies immediately; the badge on their row goes to 1 single server.
db-01 and nothing else in that group. No team membership, no group grant.This grant sits on top of whatever their teams already give them, and it is the same panel you use to take it away again: untick, and the access is gone at once. Any session they already have open is not killed by the change — end it from Activity if that matters.
The same row also holds Select every server in a group, which grants that whole group to this one person without creating a team for them. Use it when one person genuinely is the exception; use teams when there will ever be a second person.
Single-server grants are the layer that quietly rots. They are invisible in the team view, so someone keeps a server long after the reason expired. Check User access every few months, or use just-in-time access instead, which expires on its own.
Who can reach what
At the bottom of Groups → User access is Effective access. Pick a person and it lists every server they can reach right now, and which rule put it there.
This is the answer to "why can she reach that?" and to "did we actually remove his access?". Use it after any change to access, and when an audit asks.
Database access
Database access follows the same shape as servers, with one extra choice: read-only or read and write.
- Put connections in database groups when you create them.
- Open Groups → User groups and expand a team.
- Under Database groups this team reaches, set each group to No access, Read-only or Read & write.
A read-only grant is enforced by the database itself: the query runs inside a read-only transaction, so anything that would write is refused by the engine rather than by a checkbox. The row editor is hidden and the read-only switch in the SQL console is locked on. A crafted request cannot get around it.
Creating, editing and deleting connections stays with administrators, whatever grants a team holds.
Switching modules off for one person
The Add and Edit user dialog has three switches that take capability away from that one account, whatever their groups say:
- Browser terminal — off means they use their own SSH client only. Handy for people who should not have a terminal in a browser tab on a shared laptop.
- Files — off removes the file manager.
- Database — off removes the database browser. Only shown on Enterprise, where the module exists.
These are subtractions, not grants. Switching Files on for someone who reaches no servers still gets them nothing.
Just-in-time access
Standing access is the thing audits complain about. Just-in-time access, a Pro and Enterprise module, lets someone ask for a server for a set number of hours; an admin approves, and the grant removes itself when the clock runs out.
Asking: JIT Access → Request access, pick a server, choose how long, say why. The reason is not decoration — it is what the approver reads and what stays in the log.
Approving: admins see waiting requests on the dashboard and on the JIT page, and can approve, shorten or decline with a note. An approved grant shows a bar draining towards its end time, and turns amber in the last stretch. Revoke early with one click.
When the window closes the access disappears on its own. What remains is the request, the approval, the reason and the session recording it produced.
Browser terminal
A real terminal, in the browser, on any server you may reach. Open it from the Terminal page, from the SSH button on a server's row, or with Ctrl+K from anywhere.
Every server you open gets its own tab, so you can work on three machines at once without three windows. The toolbar searches the output, copies all of it, downloads the transcript, clears the screen and goes full-screen. The zoom control and the light-and-dark switch are per session.
Sessions survive a hiccup: if the connection drops, an overlay offers to reconnect rather than throwing the tab away.
Everything the screen showed is recorded, keystrokes are not. Passwords typed into sudo do not appear in a recording, because the screen never showed them.
Your own SSH client
People who live in a terminal do not have to use the browser. Point your own client at the gateway:
ssh [email protected] -p 2222
The gateway asks for up to three things, in this order:
- Your SSH key, which must be registered on your profile.
- Your password, the same one as the web interface.
- Your two-factor code.
Then you get a menu of the servers you may reach. Pick one and you are on it, with the session recorded exactly like a browser session. Sessions end the way SSH sessions always do — exit, or Ctrl+D.
How many of those three are asked for is a policy under Settings → Security → SSH key policy:
| Required | Key, then password, then code. Three factors. This is the default and the one to keep. |
|---|---|
| Optional | A key-holder may skip the key and sign in with password and code. |
| Off | Keys are ignored: password and code only. |
Add your key yourself under Profile → SSH keys, or have an administrator paste it when they create your account.
You have two minutes to get through the prompts. If your key has a passphrase and your authenticator is on a phone in another room, run ssh-add first so the passphrase is not part of the clock.
Files
The file manager, a Pro and Enterprise module, is SFTP over the same connection as the terminal. Browse, upload, download, rename, delete, change permissions. Open it from the Files page or the Files button on a server's row.
The same access rules apply: you see the servers you may reach and nothing else. Every upload and delete is written to the audit log with your name on it.
Browsing a database
With the Enterprise module and a connection you may reach, Database gives you three tabs.
Structure lists the columns, their types, nullability and keys. SQL is a console with a read-only switch.
Read-only is a real read-only transaction, not a keyword check, so a write hidden inside a common table expression is refused just as plainly as an obvious one. Turn it off deliberately when you mean to change data — and if your grant is read-only, the switch stays locked on.
A table without a primary key can be read but not edited: Portalis will not update a row it cannot point at unambiguously. Use the SQL console for those.
Command guard
The command guard, a Pro and Enterprise module, watches what is typed in a session and acts on patterns you write.
| Block | The command never reaches the server. The session shows [ezssh] command blocked by policy and the attempt turns up red in Activity. |
|---|---|
| Warn | The command runs, and the attempt is flagged amber in Activity for someone to look at. |
Anchor patterns so they match a command and not a mention of it. ^\s*rm\s+-rf\s+/ catches the disaster; rm -rf unanchored also catches the sentence someone pasted into a comment.
The guard is a seatbelt, not a wall. Someone determined can wrap a command in a script, encode it, or spell it differently. It stops the accident and it records the attempt. It does not turn an untrusted person into a safe one — that is what access control is for.
Activity
Everything that happened, in one list: sign-ins and failed sign-ins, sessions opened and closed, files moved, queries run, guard hits, access changed, licence applied.
Click a row to expand the whole event: who, when, from which address, on which server, and the detail the event carried. Export the filtered view as CSV when someone needs it in a spreadsheet.
The Sessions tab shows what is running right now, and lets an admin watch a live session read-only or end it.
Audit events are kept for a year by default and swept nightly. Change that under Settings → Authentication if your retention rules differ.
Recordings
Every session through Portalis is recorded — the browser terminal and the SSH gateway both. Recordings is where they live.
Play one and it replays at its original speed, in a terminal, exactly as it looked. Download gives you the raw .cast file, which asciinema opens. Filter by user, server or date when you are looking for something specific.
Recordings are terminal casts, not video: a long session is usually well under a megabyte. Storage and retention live under Settings → Storage, covered below.
Settings, tab by tab
Settings is grouped into Instance, Access and Appearance. Every tab has a ? next to its heading with the tips for that screen.
License & Modules
Your plan, the key this instance runs on, how many servers it covers and which modules are on. Applying a new key replaces the current one straight away, without a restart. Switching a module off hides it for everyone including admins; your licence is untouched and you can switch it back on any time.
Update
Portalis checks for updates daily. When one is available this tab shows the release notes and a button to apply it. See Updating below for what that does.
Storage
Disk usage shows how many recordings are on disk, what they weigh and how much room is left on the host. Retention decides how long they live: 7, 14, 30 or 90 days, a number of your own, or forever. A nightly sweep removes what is older, and Delete expired now does it on the spot after telling you how much will go.
The default is seven days. Deleting a recording leaves the session itself in Activity — only the replay file goes.
If your industry requires keeping session records for a fixed period, set the retention to match before the first nightly sweep runs. Set it to Keep forever if the rule is open-ended, and watch the disk instead.
Security
The gateway key is here to copy. The SSH key policy decides whether a registered key is required, optional or ignored on port 2222. The IP allowlist, when switched on, refuses everything from outside the ranges you list — the web interface and the gateway both.
Add your own address to the allowlist before you switch it on. Locking yourself out means editing the database by hand on the host.
Authentication
Username and password, SSH key and TOTP are part of the gateway and cannot be switched off. Below them sits the password policy — minimum length, and whether a digit, a symbol or a capital is required — which is checked whenever anyone sets a password. Existing passwords keep working until they are changed.
Single sign-on (Enterprise) connects an OpenID Connect provider: Authentik, Keycloak, Okta, Entra ID, Google Workspace. Portalis reads the provider's discovery document at <issuer>/.well-known/openid-configuration, so a typo in the issuer fails when you save rather than when a user tries to sign in. SSO covers the web interface; the SSH gateway still uses password plus code, because SSH has nowhere to put a browser redirect.
API keys
Create a key, choose read-only or read-write, and copy the secret — it is shown once and stored hashed. Revoking a key takes effect immediately. Calls made with a key appear in the audit log under that key's name.
Branding
Change the name, the accent colour, the logo and the sign-in background. Useful when the gateway carries your own company's name in front of customers. The preview updates as you type.
Your own profile
Click your name at the bottom of the sidebar. Four things live here.
- Password — change it; the policy above is checked as you type.
- SSH keys — add the public keys you use for port 2222. These are not the keys Portalis uses to reach your servers; that is the gateway key and it is separate.
- Two-factor and sessions — reconfigure your authenticator, get fresh recovery codes, and sign out everywhere at once if you left a session on a machine you no longer trust.
- Preferences — light or dark, and whether the tips appear.
Updating
Two ways, same result.
From the interface. Settings → Update → Update now, for super admins. Portalis pulls the new image, restarts, and shows the steps as it goes. If the new version does not come back up, the previous image is restored automatically.
From the shell. Re-run the installer. It keeps your .env, your database and your recordings, pulls the new image and restarts:
curl -fsSL https://get.portalis.sh | sudo bash
Updates are ordinary Docker image swaps. Database migrations run at start-up and are logged. Skipping versions is fine — migrations from any earlier version run in order.
Take a backup before a major update. It is one command, it takes seconds, and it turns a bad afternoon into a five-minute rollback.
Backups
Three things are worth keeping, and they live next to each other:
| The database | Users, servers, groups, grants, audit log. Everything except the recordings themselves. |
|---|---|
/opt/portalis/.env | The secrets. Without it a restored database cannot be read: the stored server passwords are encrypted with a key that lives here. |
| The recordings volume | Only if you must keep session replays. It is the big one. |
# database + secrets, the two that matter
cd /opt/portalis
docker compose exec -T db pg_dump -U ezssh ezssh | gzip > ~/portalis-$(date +%F).sql.gz
cp .env ~/portalis-env-$(date +%F).bak
Restoring is the same in reverse: put back .env, load the dump into a fresh database, start the stack.
Keep .env as safe as the database itself. It holds the key that decrypts stored credentials, and a copy of both together is a copy of everything.
API and keys
Everything the interface does goes through a REST API at /api/v1, and an API key reaches the same endpoints. Create one under Settings → API keys and send it as a bearer token:
curl -H "Authorization: Bearer <your-key>" \
https://portalis.example.com/api/v1/servers
Read-only keys are refused on anything that writes. Both kinds are logged with the key's name, so an automation's actions are as traceable as a person's.
Uninstalling
# stop and remove the containers and units, keep the data
curl -fsSL https://get.portalis.sh | sudo bash -s -- uninstall
# the same, and delete the database and the recordings too
curl -fsSL https://get.portalis.sh | sudo bash -s -- uninstall --purge
Without --purge your data stays in place and re-running the installer picks it up again. With it, everything goes. Nothing has to be removed from the servers you connected to except the gateway key line in authorized_keys, if you want it gone.
Security model
What Portalis does with your credentials, and what it expects from you.
What is stored, and how
| Server credentials | Encrypted with a key in /opt/portalis/.env. A stolen database without that file is not enough to reach your servers. |
|---|---|
| User passwords | Hashed with Argon2id. They are never stored or logged in a readable form, and Portalis cannot show them to anyone, including you. |
| The gateway key | A private key on the Portalis host. This is the crown jewel: whoever holds it can reach every server that trusts it. |
| Recordings | Plain files on disk under /var/lib/ezssh/recordings. They contain what the screen showed, so treat them as sensitive. |
| Two-factor secrets | In the database. Recovery codes are single-use and consumed when used. |
What guards the doors
- Mandatory two-factor. No account reaches anything before it enrols, on the web and on the gateway alike.
- Three factors on port 2222 by default: registered key, password, code.
- Per-IP backoff. Repeated failures ban the source for a while, doubling with each failure, so a password is not worth guessing at.
- Host key pinning. A server whose key changed is refused until an administrator clears the pin.
- Everything audited. Sign-ins, failures, sessions, file moves, queries, access changes.
What is on you
- Put HTTPS in front of the web interface. Portalis does not terminate TLS itself.
- Keep the host patched and its Docker socket private. Root on the host is root on the gateway key.
- Back up
.envseparately from the database, and keep both away from the people whose access you are limiting. - Review User access now and then. The single-server grants are the ones that outlive their reason.
Troubleshooting
The installer stops on Docker
Docker or Compose v2 is missing and there is no terminal to ask in. Re-run with PORTALIS_INSTALL_DOCKER=1, or install Docker yourself and run the installer again.
A server shows Unreachable
Reachability is a TCP check every five minutes; Check now tests it immediately. Unreachable means Portalis could not open the port: wrong address, firewall, or the machine is down. Portalis has to reach the server, never the other way round.
"Permission denied (publickey,keyboard-interactive)" on port 2222
That is the key policy doing its job. Either add your public key under Profile → SSH keys, or have an administrator set the policy to Optional or Off under Settings → Security.
The gateway closes while I am typing the code
You have two minutes from connecting to finish all the prompts. Load your key into the agent with ssh-add first, and have the authenticator open. A cut-off attempt is logged as gateway.challenge_aborted, not as a wrong code.
Adding a server fails on authentication
The gateway key is not in the target account's authorized_keys, or it is on the wrong account. Copy the key from Settings → Security again, and check you are appending it for the same user you typed in SSH username. Adding the server with a password once lets Portalis install the key for you.
Someone cannot see a server you granted
Open Groups → User access → Effective access and pick them. If the server is not listed, no rule grants it. If it is listed but they still cannot connect, check whether their account has the browser terminal switched off, and whether the server is enabled.
The database page is empty
Database access uses its own connections, not your servers. An administrator adds a connection under Database → New connection, and a team is granted its group under Groups → User groups. Without both, the page has nothing to show.
Paid features stopped working
The licence check-in has not reached the licence server for over a week. Check the host's outbound connectivity to my.portalis.sh. The banner on the dashboard says when the last successful check-in was.
Reading the logs
cd /opt/portalis
docker compose logs -f portalis # the application
docker compose logs db # the database, incl. migrations
cat /var/log/portalis-install.log # what the installer did
FAQ
Does anything have to be installed on the servers?
No. Portalis connects over SSH like any client. The only change on a server is one line in authorized_keys.
What happens to open sessions when I revoke access?
They keep running. Revoking stops the next connection, not the current one. End a live session from Activity → Sessions when it needs to stop now.
Can two people work on the same server at once?
Yes, each in their own session, each recorded separately.
Are keystrokes recorded?
No. Recordings hold what the screen showed. Commands are visible because the shell echoes them; a password typed into a prompt is not, because the screen never showed it.
Can I use my own SSH key to reach the servers?
Your key gets you into the gateway. From the gateway onwards Portalis uses its own key to reach the servers — that is what keeps the servers' credentials out of everyone's hands.
Does Portalis work with a bastion I already have?
It is one. Point it at the servers behind your bastion, or replace the bastion with it. What it will not do yet is chain through another jump host to reach a target.
Is there a limit on users?
No. Plans count servers, not seats. Adding the whole team costs nothing extra.
What happens if the Portalis host dies?
Nobody can reach the servers through it until it is back, which is the trade a gateway makes. Restore from your backup onto a new host, or keep a break-glass account on the critical servers for exactly that day.
Can I run it air-gapped?
Yes. It needs an air-gapped licence key so it does not expect to reach the licence server, and you pull image updates yourself. Ask us for one.
Where do I report a problem?
Through your account at my.portalis.sh, or the contact form on portalis.sh. For anything security-related, say so in the subject and we will handle it privately.
Portalis