Skip to content
How-to guide

Manage SSH access

Open an App and select SSH. SSH access is configured per App and requires both a registered public key and an allowed source IP address. An App can have up to 32 SSH users.

The SSH users card for an example App, showing an enabled deploy user with two truncated synthetic key fingerprints, enabled file-transfer and database-access controls, a provisioned database user with a password form, and the connection command above.
  • Team owners and admins can manage every SSH user, key, add-on access grant, database account, and source IP range.
  • Developers can add SSH users and manage only users they created, including enabling, disabling, removing, and changing those users’ keys.
  • Developers can’t grant file transfer or database access and can’t change the App-wide IP allowlist.
  • Billing members can review the page but can’t change SSH access.

If the portal can’t load your Team role, it hides management controls rather than offering actions that would fail.

  1. Under Add an SSH user, enter a login name and select Add user.
  2. Open the new user’s card.
  3. Paste a complete OpenSSH public key under Add a public key.
  4. Add an optional label such as laptop or workstation.
  5. Select Add key.

The card shows each key’s fingerprint and type. A user with no keys can’t authenticate. Public keys aren’t passwords, but you should still remove keys for lost or retired devices promptly.

The IP allowlist applies to every SSH user on the App. Team owners and admins can add a single address or a CIDR range and attach a label.

IPv4 ranges must be /16 or narrower, and IPv6 ranges must be /48 or narrower. Ranges that cover the entire internet are never accepted.

If Use my IP is available, verify the detected address before adding it. VPNs, mobile networks, and office gateways can change the public address. Add a replacement range before removing the one you are currently using so you don’t lock yourself out.

Use the copy action beside the SSH user to copy the current command. The portal provides the resolved App identifier, login name, and SSH host. Don’t reconstruct the command from the browser URL because the URL can contain an App slug while SSH authentication uses the resolved App identifier.

SSH uses public-key authentication on port 22. Before troubleshooting the client, confirm that the user is enabled, has a key, and your current source IP is covered by the allowlist.

The managed shell includes the synsmarts CLI and framework tools appropriate for the App. See Use the synsmarts CLI, Operate WordPress, or Operate Magento.

An agent, bot, or CI job can add and remove its own SSH access without a portal session when the credential you create for it carries the SSH permissions. The same three permissions apply to an API token (API tokens) and to an MCP grant (MCP access, the Agents, bots & service accounts tab):

  • Read SSH access (ssh.read) lists users, keys, and the allowlist.
  • Manage SSH users (ssh.write) creates and removes SSH users and adds their keys.
  • Manage the SSH IP allowlist (ssh.allowlist.write) allows and removes source IP ranges.

Only Team owners and admins can create API tokens and machine MCP grants. A developer’s own MCP connection can carry Read SSH access and Manage SSH users, but not Manage the SSH IP allowlist: the App-wide allowlist stays with owners and admins, as in the portal.

With a token, the CLI runs the same operations the portal does; with an MCP grant, the agent calls the matching tools (list_ssh_users, create_ssh_user, add_ssh_key, add_ssh_allowlist_entry, remove_ssh_user, remove_ssh_allowlist_entry, list_ssh_allowlist):

Terminal window
synsmarts ssh users create ci-deploy --json
synsmarts ssh keys add <user-id> --public-key "$(cat ~/.ssh/id_ed25519.pub)"
synsmarts ssh allowlist add 203.0.113.7/32
synsmarts ssh users remove <user-id>
synsmarts ssh allowlist remove <entry-id>

A credential manages only the SSH users and allowlist ranges it created: it can’t add a key to a person’s SSH user, remove one, or remove a range a person added. Revoking the token or MCP connection removes the SSH users and ranges it created (rotating keeps them), and owners and admins can remove them at any time, so cutting an agent’s access is always one portal action.

A credential reaches only user create and remove, key add, and the allowlist. File-transfer and database grants, MySQL accounts, key removal, and enabling or disabling a user stay in the portal. The managed shell’s own SYNSMARTS_API_KEY doesn’t carry SSH access, so a command inside the shell can’t add SSH users.

synsmarts support staff and platform agents can open their own time-limited SSH user in your App to investigate a ticket or verify a deploy. Such a user shows on this page with a platform marker, the staff identity, the reason, and when it expires (4 hours by default, 24 hours at most). It’s removed when the work ends or when it expires, and every open, close, and expiry is recorded in your App’s audit events. Owners and admins can disable or remove it at any time; its keys, grants, and database access can’t be changed from your side.

Append a quoted command to the copied SSH target:

Terminal window
ssh <copied-SSH-target> 'echo ok'
printf x | ssh <copied-SSH-target> 'wc -c'
ssh <copied-SSH-target> 'cat /var/www/html/example.bin' > example.bin

One-shot commands run in the managed shell pod, alongside interactive SSH, with the writable Workspace at /var/www/html. A code deploy that preserves the shell configuration leaves the command running while serving pods change. A shell restart, PHP-line change, node failure, or lost connection can still interrupt it.

Standard output and standard error stream separately, preserving binary bytes. Input and its end-of-file marker reach the command. The command’s exit code reaches your client; a missing exit status is reported as a failure. Output has no size cap. A connection that exceeds one hour ends with exit code 124 and a message on standard error.

The managed shell stops 15 minutes after the last session closes. Work you leave running, such as a detached tmux session or a long Composer run, keeps it running for up to 24 hours after the last session; after that the shell stops and ends that work.

After the managed shell idles, a connection waits up to five minutes for it to start. Progress messages appear every ten seconds, on standard error for one-shot commands and file transfers. If the wait ends, retry after the delay shown. A startup error names the failing state; contact support if it repeats.

Select Disable to suspend a user without deleting their keys. Select Enable to restore access after reviewing the user’s keys and source ranges.

Removing a user deletes all of that user’s keys and revokes their add-on access. Removing the last key blocks only that user; removing the last IP range blocks every SSH user on the App.

When these controls appear for your App, file transfer and database access are per-user grants and are off by default. Only Team owners and admins can change them.

  • File transfer (SFTP/SCP) allows the user to upload and download files under /var/www/html, the writable workspace path. The first connection can take longer while the managed shell starts. On Magento 2, pub/media under that path is the App’s media storage, with its own upload rules (see Upload Magento 2 media).
  • Database access (MySQL) allows an SSH local-forward connection to this App’s database using a platform-assigned, per-user database account.

After enabling MySQL access, wait for the database account to reach provisioned. Set a password between 12 and 256 characters, then select and save the schema privileges the user requires. The password is write-only: the portal reports whether one is set but never displays it. If you save no privileges, the account can connect but can’t read or write data.

Copy the assigned username from Database user. Open a local forward whose requested destination port is 3306, for example:

Terminal window
ssh -N -L 13306:mysql:3306 <copied-SSH-target>

Then connect the database client to 127.0.0.1:13306 with the assigned username and the password you set. Set your client’s SSL/TLS mode to Disabled. No certificate or TLS setup is required — the SSH tunnel already encrypts and authenticates the connection, so a MySQL-layer certificate is redundant. Leaving SSL enabled can also make some GUI clients (for example TablePlus) fail to verify the App database’s internal certificate. The platform derives the actual App database destination from the authenticated SSH user; it doesn’t dial the client-supplied mysql hostname.

Password, privilege, and grant changes reconcile asynchronously. Wait for the account state or operation status to finish before testing the new access. If you lose the password, set a new one; it can’t be retrieved.

The scoped account isn’t the App’s database root account. Grant only the privileges required for the user’s task.

On a Magento 2 App, /var/www/html/pub/media in the managed shell and over SFTP or SCP is the App’s media storage, the same files the storefront serves under /media/. A file you write there is live once the upload finishes. It isn’t a Workspace folder: it isn’t part of a deploy, and App-file snapshots don’t cover it. Magento 1 Apps keep pub/media in the Workspace, and none of this section applies to them.

Uploading needs the File transfer (SFTP/SCP) grant, or a shell session for rsync and cp. Each file is stored as a whole object when the writing program closes it, so media storage accepts only these operations:

  • Create a file, or replace a whole file.
  • Delete a file. A folder disappears when its last file is deleted.
  • Create a folder. A new folder exists only in your session until a file is written into it.

Media storage refuses renames, edits in place, appends, chmod, chown and timestamp changes, and the refused operation fails with a permission or input/output error. Every file reads as owned by the App user with mode 0644, and no file in pub/media is executable.

A plain SFTP put and scp work as they are. Turn off any client feature that uploads to a temporary name and renames it afterwards: WinSCP’s Transfer to temporary filename is one. The rename fails, and the temporary file (for example image.jpg.filepart) stays in media storage until you delete it. Resuming an interrupted upload and preserving permissions or timestamps fail for the same reasons; upload the whole file again instead.

Run rsync inside the managed shell, or point it at the shell’s SSH target from your computer; either way the writing side is the shell. Use a temporary folder outside pub/media and skip the attributes media storage can’t keep:

Copy media into a Magento 2 App
mkdir -p ~/.rsync-tmp
rsync -r --no-times --no-perms --no-owner --no-group \
--temp-dir="$HOME/.rsync-tmp" \
./media/ /var/www/html/pub/media/

From your computer, the destination is <copied-SSH-target>:/var/www/html/pub/media/ and the temporary folder is the same path inside the shell. Don’t use -a, --inplace, --partial or --append: each one asks for an in-place write or an attribute change, and the transfer fails. The temporary folder sits in the shell’s home volume, which holds 2 GiB, so every file you copy must be smaller than that. Without --temp-dir, rsync writes a hidden temporary file next to the destination and renames it, which media storage refuses.

--delete works and deletes live media the source doesn’t have. Check the source before using it.

Open the file’s storefront URL, https://<your-domain>/media/<path>. A cached copy of an older file can show until the CDN cache refreshes.

Magento writes to the same storage when it saves images, for example during a product import. When the shell and Magento write the same file at the same time, the last write wins. Avoid bulk uploads into folders an import is writing.

A Team owner or admin can bring back an overwritten or deleted file with Restore media. Shell users can’t restore earlier versions from the shell.

Use the narrowest revocation that matches the situation:

  • remove one key when a device is retired;
  • disable a user when access may be restored later;
  • remove a user when the identity should no longer exist;
  • remove a source range when the network should no longer connect; or
  • turn off an add-on grant when the user no longer needs file transfer or database access.

Destructive actions require confirmation. Revocation blocks new authentication after the authorization change is applied and terminates existing sessions and database forwards within the revocation interval.

For a suspected compromise, disable the user first, remove the affected key, and verify that a new connection is rejected. Contact support if an existing session remains active after the revocation has finished applying.