Connect first, build next

From your first SSH
to your first archive

For developers who have just received a node: verify your connection and toolchain before adding it to your pipeline. The commands here are examples, not live status for your node.

If you already have a node, start at step 1. Credentials, expiry dates, and current availability are shown in the console.

01 / Connect

Make sure you're connecting to your physical node

Open the relevant instance in the console and verify the node, IP address, SSH port, and username. Don't copy credentials from an old support ticket or store your login password in a repository.

  1. 01

    Check instance details

    Open the instance details in the console and note the IP address, port, and current credentials. In this guide, NODE_IP,SSH_PORT and NODE_USER stand for those values. Set them in your local terminal before running commands; don't copy the variable names as literal addresses.

  2. 02

    Verify the host fingerprint

    On your first connection, SSH will ask whether to trust the host. Compare the displayed fingerprint with information from a trusted source before confirming. If you can't verify it, open a support ticket first. Don't bypass the prompt by disabling host verification.

  3. 03

    Change your password and set up a public key

    After your first login, run passwd to change the current user's password. You can copy the public key to the node, but keep the private key on your local machine. Keep your current session open, then use a second terminal to verify that public-key login works before changing authentication settings.

Save the connection details as an alias

On your local machine, add the following to ~/.ssh/config . Replace the three uppercase fields with the actual values from the instance details.IdentityFile should point to a private key file already on your local machine. If you haven't created a key yet, run the generation command shown alongside.

Host armshell-dev
  HostName NODE_IP
  User NODE_USER
  Port SSH_PORT
  IdentityFile ~/.ssh/armshell_ed25519
  IdentitiesOnly yes
  ServerAliveInterval 30
  ServerAliveCountMax 3

After saving, connect with ssh armshell-dev . If the host fingerprint changes, first confirm whether the instance was redeployed. Don't simply delete the saved record and reconnect.

Upload your public key, not your private key

Run the public-key commands below on your local machine. They assume the alias is configured and password login is available. Set a passphrase for your key and keep a secure backup locally.

ssh-keygen -t ed25519 -f ~/.ssh/armshell_ed25519
cat ~/.ssh/armshell_ed25519.pub | ssh armshell-dev \
'umask 077; mkdir -p ~/.ssh; cat >> ~/.ssh/authorized_keys'
ssh -o PreferredAuthentications=publickey armshell-dev

After logging in, run whoami,sw_vers , and df -hto check the user, macOS version, and available disk space. If permissions aren't as expected, fix them before installing development tools.

02 / Toolchain

Get an interactive build working before moving to CI

Make sure the target Xcode version is installed and its first-launch setup is complete. The commands and output below show the format; versions, SDKs, and certificate names depend on your node and project. They are not a list of preinstalled tools.

dev@armshell-tokyo
$ sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer
$ xcode-select -p
/Applications/Xcode.app/Contents/Developer

$ sudo xcodebuild -runFirstLaunch
$ xcodebuild -showsdks
iOS SDKs:
    iOS 18.0                       -sdk iphoneos18.0
iOS Simulator SDKs:
    Simulator - iOS 18.0           -sdk iphonesimulator18.0

$ bundle exec fastlane match development --readonly
[09:12:01]: Successfully decrypted certificates repo
[09:12:03]: Installing certificate...
[09:12:05]: Installing provisioning profile...
[09:12:06]: All required keys, certificates and provisioning profiles are installed

$ xcodebuild -workspace App.xcworkspace -scheme App \
    -configuration Release -destination 'generic/platform=iOS' \
    -archivePath "$HOME/build/App.xcarchive" archive
** ARCHIVE SUCCEEDED **

Xcode version selection affects the whole node

xcode-select changes the default developer directory and can affect other tasks on the same machine. When running multiple versions side by side, set DEVELOPER_DIRfor each task instead, and print xcodebuild -versionat the start of the log to prevent handoffs from using the wrong toolchain.

Make sure signing is unlocked for the current session

This example assumes the project is configured with fastlane, a Gemfile, and access to the certificate repository. Start by syncing existing certificates in read-only mode. The build user must be able to access and unlock its own keychain. If signing fails, check permissions and provisioning profiles first instead of repeatedly generating certificates.

An archive isn't a release

Confirm the workspace and scheme names, then inspect the archive. Exporting an installable package also requires export settings that match your distribution method. After the first successful build, record the Xcode version, dependency lockfile, and full command before moving the build into your pipeline.

03 / Desktop

Open a desktop session only when you need the GUI

Use the command line for transfers, builds, and log checks. Switch to the macOS GUI when setting up tools for the first time, checking keychain prompts, or debugging a graphical app. Find the desktop connection details in the instance view after you log in.

Enable access and set permissions

Connect using the desktop access provided for your instance. In System Settings → General → Sharing, check Screen Sharing. Allow access only for specified users; don't enable access for everyone. If Screen Sharing and Remote Management are mutually exclusive, check the current configuration before changing either setting.

If you can't access the desktop on your first attempt, ask for help through a console support ticket. Don't change multiple macOS remote-access services if you're unsure what they do.

Sign in to the console to open a support ticket

Client and connection options

On macOS, you can use the built-in Screen Sharing app. On other systems, use a client that supports the VNC protocol and secure connections. Prefer the secure access method provided for your instance; don't expose the desktop service directly to untrusted networks.

If you've confirmed that the node's VNC service is listening on its local port 5900, use the SSH tunnel below to connect your client to local address 127.0.0.1:5901.

ssh -N -L 5901:127.0.0.1:5900 armshell-dev

Reduce display overhead first

For your first connection, start with a single display at 1440 × 900 and turn off unnecessary animations. If bandwidth is limited, lower the image quality or frame rate first. Increasing resolution won't speed up builds; it will only use more bandwidth.

Bandwidth use varies with the encoding and how much the display changes, so no single bandwidth level is guaranteed to suit every task. If the connection stutters, pause large uploads and check whether SSH is also lagging.

The desktop and command line use the same dedicated physical machine.
04 / Continuous integration

Use the environment you've just verified for your Runner

Register the Runner under a dedicated macOS build user. Start with one concurrent job to verify dependencies, signing, and artifact transfers, then increase concurrency based on memory and disk usage. Don't send untrusted merge requests to a runner with release credentials.

GitLab: register a shell executor

Install a GitLab Runner version suitable for Apple Silicon. The commands below assume the executable is in the current user's PATH and that you've created a Runner in your CI settings and obtained the server URL and authentication token.

read -r CI_SERVER_URL
read -rs RUNNER_TOKEN
gitlab-runner register --non-interactive \
  --url "$CI_SERVER_URL" \
  --token "$RUNNER_TOKEN" \
  --executor shell
unset RUNNER_TOKEN
gitlab-runner verify
gitlab-runner install
gitlab-runner start
gitlab-runner status

In your CI settings, assign clear tags such as macos-arm64 and the Xcode version required by your project. Job tags must match the Runner. Run whoami,xcodebuild -versionfirst, then start a production build.

GitHub Actions: register a self-hosted runner

In your repository or organization settings, get the macOS ARM64 download and the registration command for this run. Download, verify, and extract the package, then run the following commands from the extracted directory. Get both variables from the settings page.

read -r REPOSITORY_URL
read -rs RUNNER_TOKEN
./config.sh --url "$REPOSITORY_URL" \
  --token "$RUNNER_TOKEN" \
  --labels macos-arm64 --unattended
unset RUNNER_TOKEN
./svc.sh install
./svc.sh start
./svc.sh status

Set your workflow to use runs-on: [self-hosted, macOS, ARM64]. Add any custom tags you've configured. Registering a token doesn't start the service; after setup, check the service status and run a real test job.

Keep launchd running: install the service, then check what's loaded

The service installation commands above create macOS service configurations according to each Runner's implementation. Prefer the management commands they provide; don't create a second service manually to run the same Runner.

A user-level LaunchAgent usually loads when that user logs in. For unattended operation after a restart, first verify how the Runner service works, the login state, and the keychain policy. Being online now doesn't guarantee the Runner will be available after a restart.

ls "$HOME/Library/LaunchAgents"
launchctl print "gui/$(id -u)"
gitlab-runner status
./svc.sh status

Run the applicable checks from the directory for each Runner. A non-interactive CI environment may not have the same PATH as your terminal. Put Homebrew, Ruby, and Xcode paths in a version-controlled build script, and never print tokens in logs.

05 / Data

Sync only what's changed, and keep backups off the node

Clone source code from your repository whenever possible. Use rsync over SSH for large directories, build artifacts, and files that don't belong in the repository. A rented node is a work environment, not your only copy of the data.

Preview before uploading

First check rsync --versionon both ends. The examples use common basic options: a trailing slash syncs directory contents, and you can adjust exclusions for your project. Review the full dry-run output before removing -n.

rsync -avzn --exclude '.git/' --exclude 'build/' \
  ./project/ armshell-dev:~/work/project/

rsync -avz --exclude '.git/' --exclude 'build/' \
  ./project/ armshell-dev:~/work/project/

rsync -avz armshell-dev:~/build/ ./artifacts/

Don't add --deletewithout confirming first: it deletes extra files on the destination. Manage access to keys, certificates, and logs containing sensitive data separately. Don't distribute them publicly with the entire working directory.

Multiple nodes aren't an internet accelerator

Thunderbolt 5 interconnects are for workflows where multiple nodes need to process large files together. They don't replace the internet connection between your local network and the node, and won't automatically speed up every rsync task. Before ordering, describe the nodes involved, data size, and workflow, then confirm which connection options apply.

If you need more workspace, you can add a +1TB SSD or +2TB SSD. Extra storage isn't a backup: keep copies of your code, artifacts, and certificates off the node.

After each release, verify that transferred files are readable and record checksums for important artifacts. Before expiry, test recovery from a separate copy at least once; don't rely only on a successful sync message.

06 / Account & billing

Add an expiry check to your pre-release checklist

Check your orders, instance expiry, invoices, and renewal options in the console. Nodes run continuously 365 days a year. You still need to manage your rental period and order status based on the records in the console.

Check your billing period and dates

Billing is available by the day, week, month, or quarter. Check the start and end times and time zone shown in the console, and leave enough time for builds, transfers, and backups. Don't wait until a long-running job is halfway through to renew, and don't assume payment will be collected automatically.

Use the order details and expiry date as your reference.

Confirm changes before migrating

Before upgrading your model, adding storage, or changing your billing period, check the options, price, and activation method available for your current order. Don't assume every change will take effect immediately on the same node. Before migrating, save your dependency list, signing configuration, and data copies.

Open a support ticket to confirm any unclear proration or activation rules.

Check your payment and order status

We accept USDT-TRC20 and Visa / Mastercard / Amex (via Stripe) only. All payments are settled in US dollars (USD); actual availability depends on the payment gateway response. For on-chain payments, verify the network, amount, and recipient details. For card payments, complete verification on the billing page.

If the status hasn't updated, open a support ticket with your order number and transaction record.
When something fails, troubleshoot layer by layer

First determine whether it's a connection issue or a job issue

When reporting a problem, include your order number, node, time and time zone, commands run, and the full error excerpt. You can share a screenshot or redacted terminal output, but never send passwords, private keys, tokens, or full payment card details.

SSH timed out. Where should you start?
  1. Check the instance details for the IP address, port, order, and running status to rule out an outdated address.
  2. On your local machine, run ssh -vvv armshell-devto distinguish a timeout, connection refusal, and authentication failure; these are different problems.
  3. Check your local network and outbound rules, then test again on another trusted network. An ICMP failure alone doesn't prove the SSH service is unavailable.
  4. If the connection stalls during authentication, check the username, private-key permissions, and public-key assignment. If it keeps timing out, submit a support ticket with redacted logs instead of repeatedly resetting the password.
Slow Xcode downloads or dependency fetches: how can you tell which?
  1. Run df -h first to check disk space. Make sure there's enough room for the installer, extraction, and the older version to coexist.
  2. Determine whether the delay is in transferring the Xcode installer, fetching dependencies, or the first build. A pause in the build log doesn't necessarily mean a download is in progress.
  3. Use a complete installer from a trusted source and verify its integrity. Avoid transferring large files at the same time as backups to prevent bandwidth contention.
  4. Record the source type, file size, start time, and error. Don't send download URLs that contain access tokens. If you need the node's network checked, include these details in a support ticket.
Remote desktop connects, but the screen is black?
  1. First check whether SSH works to determine if the whole node is unreachable or only the desktop session is affected.
  2. Check that the connecting user has Screen Sharing access and that an active graphical session is available. Lower the resolution and reconnect.
  3. If you're using an SSH tunnel, make sure the tunnel is still running in your terminal and that your client is connecting to the local forwarded port, not another port on the node.
  4. Don't force-restart a node that's building. Save any logs you can access, note whether you can see the login screen, and then open a support ticket.
Runner offline or online but not picking up jobs?
  1. Check the service status and process logs, then check your CI settings. Being able to SSH into the node doesn't mean the Runner service is running.
  2. If the Runner is online but not picking up jobs, check project permissions, Runner tags, workflow selection conditions, and protected branch settings.
  3. If it goes offline after a restart, check the launchd domain and whether the build user is logged in. Starting it manually from a terminal isn't a substitute for a persistent service.
  4. If a job fails after starting, print the user, PATH, Xcode version, and working directory, then check keychain and directory permissions. Don't make unregistering and recreating the Runner your first step.

SSH works, but the issue isn't resolved?

Send us the error along with the context

For product selection and pre-sales questions, contact us by email. For technical or billing issues with an existing order, sign in to the console and open a support ticket so we can link it to your instance and case history.