Resolving Oracle Cloud “Out of Capacity” issue and getting free VPS with 4 ARM cores / 24GB of memory (using OCI CLI)

Alexander Hitrov
8 min readJul 18, 2021

--

Update 2024: This approach is still functional, but many Reddit users now recommend upgrading to Pay As You Go (PAYG) for the best experience. With PAYG, you’ll continue to enjoy all the free benefits without any additional cost, but you’ll also receive priority for launching instances and are less likely to face “Out of host capacity” errors. Additionally, PAYG unlocks more types of OCI resources, including free Kubernetes-related infrastructure if that’s something you’re interested in. It’s important to set up budget alerts as a safety net and be mindful of the resources you deploy and their associated costs. This way, you can take full advantage of PAYG while keeping your spending in check.

Very neat and useful configuration was recently announced at Oracle Cloud Infrastructure (OCI) blog as a part of Always Free tier. Sometimes it’s complicated to launch an instance due to the “Out of Capacity” error. Here we’re solving that issue as Oracle constantly adds capacity from time to time.

Each tenancy gets the first 3,000 OCPU hours and 18,000 GB hours per month for free to create Ampere A1 Compute instances using the VM.Standard.A1.Flex shape (equivalent to 4 OCPUs and 24 GB of memory).

Starting from Oracle Cloud Infrastructure (OCI) CLI installation. If you prefer PHP, please look here in my similar tutorial https://hitrov.medium.com/resolving-oracle-cloud-out-of-capacity-issue-and-getting-free-vps-with-4-arm-cores-24gb-of-6ecd5ede6fcc.

Generating API key

After logging in to OCI Console, click profile icon and then “User Settings”

Go to Resources -> API keys, click “Add API Key” button

Add API Key

Make sure “Generate API Key Pair” radio button is selected, click “Download Private Key” and then “Add”.

Download Private Key

Copy the contents from textarea and save it to file with a name “config”. I put it together with *.pem file in newly created directory /home/ubuntu/.oci

That’s all about the API key generation part.

Setting up CLI

Specify config location

OCI_CLI_RC_FILE=/home/ubuntu/.oci/config

If you haven’t added OCI CLI binary to your PATH, run

alias oci='/home/ubuntu/bin/oci'

(or whatever path it was installed).

Set permissions for the private key

oci setup repair-file-permissions --file /home/ubuntu/.oci/oracleidentitycloudservice***.pem

Test the authentication (user value should be taken from textarea when generating API key):

oci iam user get --user-id ocid1.user.oc1..aaaaaaaax72***d3q

Output should be similar to

User Info

Acquiring launch instance params

We need to know which Availability Domain is always free. Click Oracle Cloud menu -> Compute -> Instances

Instances

Click “Create Instance” and notice which one has “Always Free Eligible” label in Placement Section. In our case it’s AD-2.

Almost every command needs compartment-id param to be set. Let’s temporary save it to env var (replace with your “tenancy” value from the config file):

export C=ocid1.tenancy.oc1..aaaaaaaakpx***mpa

Now let’s collect all needed values for launch instance command

  • availability-domain
  • shape
  • subnet-id
  • image-id
oci iam availability-domain list --all --compartment-id=$C

Output will be similar to

{
"data": [
...
{
"compartment-id": "ocid1.tenancy.oc1..aaaaaaaakpx***mpa",
"id": "ocid1.availabilitydomain.oc1..aaaaaaaalcd***m2q",
"name": "FeVO:EU-FRANKFURT-1-AD-2"
},
...
]
}

Remember, we need to pickup it’s name — the one which is free of charge(AD-2). Setting temporary env var with it’s value:

export A=FeVO:EU-FRANKFURT-1-AD-2

Then let’s review shapes:

oci compute shape list --compartment-id=$C

Here we are interested in this one — VM.Standard.A1.Flex:

{
"data": [
...
{
"baseline-ocpu-utilizations": null,
"gpu-description": null,
"gpus": 0,
"is-live-migration-supported": false,
"local-disk-description": null,
"local-disks": 0,
"local-disks-total-size-in-gbs": null,
"max-vnic-attachment-options": {
"default-per-ocpu": 1.0,
"max": 24.0,
"min": 2
},
"max-vnic-attachments": 2,
"memory-in-gbs": 6.0,
"memory-options": {
"default-per-ocpu-in-g-bs": 6.0,
"max-in-g-bs": 512.0,
"max-per-ocpu-in-gbs": 64.0,
"min-in-g-bs": 1.0,
"min-per-ocpu-in-gbs": 1.0
},
"min-total-baseline-ocpus-required": null,
"networking-bandwidth-in-gbps": 1.0,
"networking-bandwidth-options": {
"default-per-ocpu-in-gbps": 1.0,
"max-in-gbps": 40.0,
"min-in-gbps": 1.0
},
"ocpu-options": {
"max": 80.0,
"min": 1.0
},
"ocpus": 1.0,
"processor-description": "3.0 GHz Ampere\u00ae Altra\u2122",
"shape": "VM.Standard.A1.Flex"
},
...
]
}

I hope that you previously created at least VM.Standard.E2.1.Micro (AMD processor based) from the console— two of them are “Always Free”. If not, please do that — we need VNC, subnet, route table, security list etc. to exist.

oci network subnet list --compartment-id=$C

Please notice the id:

{
"data": [
{
"availability-domain": null,
"cidr-block": "10.0.0.0/24",
"compartment-id": "ocid1.tenancy.oc1..aaaaaaaakpx***mpa",
"defined-tags": {
"Oracle-Tags": {
"CreatedBy": "***",
"CreatedOn": "2021-01-26T13:51:31.332Z"
}
},
"dhcp-options-id": "ocid1.dhcpoptions.oc1.eu-frankfurt-1.aaaaaaaafh4***cvq",
"display-name": "subnet-20210126-1549",
"dns-label": "subnet01261551",
"freeform-tags": {},
"id": "ocid1.subnet.oc1.eu-frankfurt-1.aaaaaaaaahbb***faq",
"ipv6-cidr-block": null,
"ipv6-virtual-router-ip": null,
"lifecycle-state": "AVAILABLE",
"prohibit-internet-ingress": false,
"prohibit-public-ip-on-vnic": false,
"route-table-id": "ocid1.routetable.oc1.eu-frankfurt-1.aaaaaaaaqwe***p76q",
"security-list-ids": [
"ocid1.securitylist.oc1.eu-frankfurt-1.aaaaaaaaagnn***tca"
],
"subnet-domain-name": "subnet***.vcn***.oraclevcn.com",
"time-created": "2021-01-26T13:51:31.534000+00:00",
"vcn-id": "ocid1.vcn.oc1.eu-frankfurt-1.amaaaaaalox***y4a",
"virtual-router-ip": "10.0.0.1",
"virtual-router-mac": "00:00:17:***"
}
]
}

And save it to the variable

export S=ocid1.subnet.oc1.eu-frankfurt-1.aaaaaaaaahbb***faq

Listing images.

oci compute image list  --compartment-id=$C --shape=VM.Standard.A1.Flex

I prefer to have all the software needed for development out of the box — my choice is latest “Oracle Linux Cloud Developer”.

{
"data": [
...
{
"agent-features": null,
"base-image-id": null,
"billable-size-in-gbs": 14,
"compartment-id": null,
"create-image-allowed": true,
"defined-tags": {},
"display-name": "Oracle-Linux-Cloud-Developer-8.4-aarch64-2021.06.18-0",
"freeform-tags": {},
"id": "ocid1.image.oc1.eu-frankfurt-1.aaaaaaaa23zlxgcvdgb2zn4ffik6rda4g5daa5wa42svsgp4enljv4ywv6wa",
"launch-mode": "NATIVE",
"launch-options": {
"boot-volume-type": "PARAVIRTUALIZED",
"firmware": "UEFI_64",
"is-consistent-volume-naming-enabled": true,
"is-pv-encryption-in-transit-enabled": true,
"network-type": "PARAVIRTUALIZED",
"remote-data-volume-type": "PARAVIRTUALIZED"
},
"lifecycle-state": "AVAILABLE",
"listing-type": null,
"operating-system": "Oracle Linux Cloud Developer",
"operating-system-version": "8",
"size-in-mbs": 51200,
"time-created": "2021-06-24T20:36:22.659000+00:00"
},
...
]
}

Saving it’s image ID:

export I=ocid1.image.oc1.eu-frankfurt-1.aaaaaaaa23zlxgcvdgb2zn4ffik6rda4g5daa5wa42svsgp4enljv4ywv6wa

We also need to have a few small .json files:

  • instanceOptions.json
{
"areLegacyImdsEndpointsDisabled": false
}
  • shapeConfig.json (adjust the OCPUs count and RAM if needed). Possible values are 1/6, 2/12, 3/18 and 2/24, respectively. Please notice that “Oracle Linux Cloud Developer” image can be created with at least 8GB of RAM (so it should have minimum 1/8).
{
"ocpus": 4,
"memoryInGBs": 24
}
  • availabilityConfig.json
{
"recoveryAction": "RESTORE_INSTANCE"
}

In order to have secure shell (SSH) access to the instance you need to have a keypair, e.g. ~/.ssh/id_rsa and ~/.ssh/id_rsa.pub. Second one (public key) filename should be provided to a command below. The are plenty of tutorials on how to do that, we won’t cover this part here.

Finally

oci compute instance launch \
--availability-domain $A \
--compartment-id $C \
--shape VM.Standard.A1.Flex \
--subnet-id $S \
--assign-private-dns-record true \
--assign-public-ip false \
--availability-config file:///home/ubuntu/availabilityConfig.json \
--display-name my-new-instance \
--image-id $I \
--instance-options file:///home/ubuntu/instanceOptions.json \
--shape-config file:///home/ubuntu/shapeConfig.json \
--ssh-authorized-keys-file /home/ubuntu/.ssh/id_rsa.pub

You can now setup crontab to run this command e.g. every 5 minutes. Create .sh script file (“exporting” needed params values before the actual command call) and make sure cron user is able to access private key. We won’t cover this part.

Output (I tested with VM.Standard.E2.1.Micro shape to not remove my existing ARM instances):

Launch instance output #1
Launch instance output #2

I believe it’s pretty safe to leave the cron running and check cloud console once per few days. Because when you’ll succeed, usually you won’t be able to create more instances than allowed — but start getting something like

{
"code": "LimitExceeded",
"message": "The following service limits were exceeded: standard-a1-memory-count, standard-a1-core-count. Request a service limit increase from the service limits page in the console. "
}

or (again)

{
"code": "InternalError",
"message": "Out of host capacity."
}

At least that’s how it worked for me.

If you switched to “Pay as you go” plan, you must consider how to stop OCI CLI API calls after you succeed–to not run into unintended charges. For example, you can setup a command

oci compute instance list --compartment-id $C

…somehow check it’s output periodically to know when cron needs to be disabled. That’s not related to our issue here.

If you want safety way with such check there is one which I introduced with PHP here https://hitrov.medium.com/resolving-oracle-cloud-out-of-capacity-issue-and-getting-free-vps-with-4-arm-cores-24gb-of-6ecd5ede6fcc

Assigning public IP address

We are not doing this during the command run due to the default limitation (2 ephemeral addresses per compartment). That’s how you can achieve this. When you’ll succeed with creating an instance, open OCI Console, go to Instance Details -> Resources -> Attached VNICs by selecting it’s name

VNICs

Then Resources -> IPv4 Addresses -> … -> Edit

IPv4 Addresses

Choose ephemeral and click “Update”

Edit IP address

Conclusion

That’s how you will login when instance will be created (notice opc default username)

ssh -i ~/.ssh/id_rsa opc@ip.add.re.ss

If you didn’t assign public IP, you can still copy internal FQDN or private IP (10.x.x.x) from the instance details page and connect from your other instance in the same VNIC. e.g.

ssh -i ~/.ssh/id_rsa opc@instance-20210714-xxxx.subnet.vcn.oraclevcn.com

Thanks for reading!

--

--