This walks through a first instance the way you would do it at work: from the CLI, with a key you control, and without leaving SSH open to the internet.
Step 1: an SSH key pair
ssh-keygen -t ed25519 -f ~/.ssh/oci_lab -C "oci-lab"
cat ~/.ssh/oci_lab.pub
Keep the private key (oci_lab) on your machine only. OCI just needs the public key.
Tip
Some older tools and images expect RSA keys. If a connection fails with an algorithm error, create an RSA 4096 key instead (
ssh-keygen -t rsa -b 4096).
Step 2: choose a shape
Flexible shapes let you pick OCPUs and memory separately.
oci compute shape list --compartment-id $COMP \
--query 'data[?contains(shape, `Flex`)].{shape:shape, ocpuMax:"ocpu-options".max, memMax:"memory-options"."max-in-g-bs"}' --output table
| Shape family | CPU | Notes |
|---|---|---|
VM.Standard.E*.Flex | AMD | General purpose default |
VM.Standard.A1.Flex | Ampere Arm | Good price/performance; needs Arm images |
VM.Standard3.Flex | Intel | When software requires Intel |
An OCPU is a physical core (two vCPUs on x86). Check current shape availability in your region and service limits before you plan.
Step 3: find an image
oci compute image list --compartment-id $COMP \
--operating-system "Oracle Linux" --operating-system-version "9" \
--shape VM.Standard.E5.Flex --sort-by TIMECREATED --sort-order DESC \
--query 'data[0].{name:"display-name", id:id}'
Filtering by shape returns only compatible images (x86 vs Arm).
Step 4: launch
AD=$(oci iam availability-domain list --compartment-id $COMP --query 'data[0].name' --raw-output)
oci compute instance launch \
--compartment-id $COMP --availability-domain "$AD" \
--display-name lab01 \
--shape VM.Standard.E5.Flex --shape-config '{"ocpus":1,"memoryInGBs":8}' \
--image-id $IMAGE --subnet-id $SUBNET \
--assign-public-ip false \
--ssh-authorized-keys-file ~/.ssh/oci_lab.pub \
--wait-for-state RUNNING
Get the private IP:
oci compute instance list-vnics --instance-id $INSTANCE --query 'data[0].{private:"private-ip", public:"public-ip"}'
Step 5: connect
Default users by image:
| Image | User |
|---|---|
| Oracle Linux, CentOS-based images | opc |
| Ubuntu | ubuntu |
| Windows | opc with a one-time password shown in the console |
Through the Bastion service (recommended)
Create a bastion once for the subnet's VCN, then a managed SSH session per connection. The instance needs the Bastion plugin enabled in the Oracle Cloud Agent.
oci bastion session create-managed-ssh \
--bastion-id $BASTION --target-resource-id $INSTANCE \
--target-os-username opc --ssh-public-key-file ~/.ssh/oci_lab.pub \
--session-ttl 3600 --wait-for-state SUCCEEDED
oci bastion session get --session-id $SESSION --query 'data."ssh-metadata".command' --raw-output
The returned command contains a ProxyCommand; replace <privateKey> with ~/.ssh/oci_lab and run it.
Directly (lab only)
If the subnet is public and the instance has a public IP:
ssh -i ~/.ssh/oci_lab opc@<public-ip>
Warning
Opening port 22 to
0.0.0.0/0invites constant brute-force attempts. If you must, limit the security rule to your own public IP.
First things on the box
sudo dnf -y update # Oracle Linux
sudo timedatectl set-timezone UTC
df -h; free -m; nproc
sudo systemctl status oracle-cloud-agent
Common problems
| Problem | Fix |
|---|---|
Permission denied (publickey) | Wrong user for the image, or wrong private key (-i) |
| Timeout | Security list/NSG, no route, no public IP, or firewalld |
Out of host capacity at launch | Try another AD or fault domain, another shape, or smaller size |
| Lost the private key | Use the serial console or Run Command to add a new key; see the rescue posts |
Clean up
oci compute instance terminate --instance-id $INSTANCE --preserve-boot-volume false --force
Prevent it next time
- Use Bastion for access and keep instances without public IPs.
- Store team keys in a vault, and add each engineer's own key rather than sharing one.
- Tag instances with owner and expiry so lab machines get cleaned up.