Oracle Cloud Infrastructure (OCI) Compute instance has been running a very generous Always Free tier for over 6 years, and the offering is just getting better, with the high-performant Arm-based Ampere offering 4 cores and 24GB RAM that can be split into 1-4 VMs.
In this post, let’s run a Linux VM with a web server on this Ampere platform using the maximum allowance (4 cores, 24GB RAM, 200GB boot volume). In the next post, we will deploy a django app to this VM.
Here’s a rundown of what we will do in this post:
- Sign up for Oracle Free Tier account
- Provision an ARM-based Compute instance
- Setup security and network connectivity for this VM to accept incoming traffic
- Connect to the VM through SSH
- Setup nginx to process incoming HTTP requests
Why Oracle Cloud?
The primary reason is the extremely generous offering of its Free Tier compared with other providers. Unlike other providers whose free services (e.g., VM, storage, database) expire in 12 months or after a fixed credit is used up in 90 days, OCI’s free tier resources never expires with frequent usage. Here’s a partial list of what they offer:
- 2 x
VM.Standard.E2.1.Micro: AMD 1/8 OCPU and 1 GB memory VM.Standard.A1.Flex: 4 ARM-based Ampere A1 cores and 24GB memory that can be distributed among 1-4 VMs- 200GB total storage (each VM requires 50GB minimal boot volume - limiting you to a total of 4 VMs) with backup
- 20GB unstructured storage
- 10TB outbound data transfer per month
- Networking: 2 VCNs with unlimited public/private subnets, 1 internet gateway (direct public internet access for egress traffic) and 1 reserved public IPv4 address. Note that NAT gateway (for private subnets to initiate outbound internet connections) and service gateway (connecting private subnets to other OCI resources) are not included in the free tier.
- Site-to-site VPN: 50 IPSec connectionS between your on-premises network and your virtual cloud network (VCN).
- MySQL Heatwave with the interesting in-database LLMs, and AutoML. 50GB storage.
- Container Registry (private repo with 10GB storage)
- Kubernetes Engine (free control plane in Basic Cluster, workers nodes can come from the free Compute instances)
- 2 Autonomous Database instances, with vector store
- Monitoring, logging and notification
- Load Balancer with 10 Mbps bandwidth, providing automated traffic distribution from a single entry point to multiple servers.
- 5 Bastions to create and manage restricted and time-limited secure access from preconfigured IP addresses to resources in private subnets.
Setup an OCI Compute VM on Arm Ampere A1
This is a shortened version of the official tutorial to create a VM instance on OCI.
By the end of this section, our OCI resources will all be setup in their right place, like this diagram: 
- Region: An OCI region is a localized geographic area that contains one or more data centers, hosting availability domains.
- Availability domains: ADs are standalone, independent data centers within a region. The physical resources in each availability domain are isolated from the resources in the other availability domains, which provides fault tolerance.
- Virtual cloud network (VCN): A VCN is a customizable, software-defined network that you set up in an OCI region. All your provisioned OCI resources sit inside a VCN.
- Subnets: You can segment a VCN into subnets. Each subnet consists of a contiguous range of addresses that don’t overlap with the other subnets in the VCN. A subnet can be public or private. Consider your traffic flow and security requirements when designing subnets. Attach all the resources within a specific tier or role to the same subnet, which can serve as a security boundary. > We will create our Compute and Storage instances in a public subnet.
- Security lists: For each subnet, you can create security rules that specify the source, destination, and type of traffic that is allowed in (ingress) and out (egress) of the subnet.
- Internet gateway: An internet gateway allows traffic between the public subnets in a VCN and the public internet.
create a free-tier account
create a VCN
- In the OCI Console, click the ☰ menu → Networking → Virtual cloud networks. Click Actions → Start VCN Wizard. > VCN can be created manually or using the Wizard. The latter is a more convenient approach as it will create a VCN with all resources (internet gateway, subnets, route tables, etc.) configured by default. The manual method will only setup a VCN with the subnet we specify, skipping all dependent resources which we have to create manually.
- Select Create VCN with Internet Connectivity to create a VCN with a public subnet that can be reached from the internet via an Internet gateway. It will also creates a private subnet.
> Note: You will notice the topology diagram and text include a NAT gateway (connecting private subnets to the internet) and a service gateway (connecting private subnets to the Oracle Services Network: e.g., Autonomous Database, Object Storage, etc.). However, in the next screen, both are removed. This is because NAT and SG are not included in the free tier.
- In the Wizard:
- Give it a name
- Configure VCN: IPv4 CIDR block:
10.0.0.0/16 - Configure public subnet: IPv4 CIDR block:
10.0.0.0/24 - Configure private subnet: IPv4 CIDR block:
10.0.1.0/24
- Click Next. You can review all the settings. Notice:
- an Internet gateway is automatically created
- Security lists for both subnets are configured for both ingress and egress traffic
- A route table pointing all outgoing traffic to the Internet gateway is created Click Create to create the VCN.
provision a VM
- Go back to the OCI Console. Click the ☰ menu → Compute → Instances → Create Instance.
- Enter a name, select an Always Free–eligible region.
- Select a compartment for your VM instance. You can use compartments to organize and isolate your OCI resources to make it easier to manage and secure access to them.
- Under Image → Shape, click Change shape, pick Ampere, then VM.Standard.A1.Flex.
- Adjust the OCPUs and memory sliders (e.g. 4 OCPUs / 24 GB to stay always free). > You can launch compute instances with predefined shapes that meet your resource requirements for CPU, memory, network bandwidth, and storage.
- Click Change Image, choose from a list of Always Free–eligible image that is compatible with the ARM platform. In our case, accept the default Oracle Linux 9, or any of the supported Ubuntu images. > note: Only platform images published by Oracle after the A1 shape release are compatible without custom modifications. Custom images can also be used if you add and have shape‐compatibility entries added. > you can also check out a performance benchmark for the available distros here. > Click Next.
- Accept the default settings for Security. Click Next.
- Under Networking, choose: Select existing VCN and choose the VCN we created in the last section. In Select existing subnet, pick the subnet we created before.
- In Primary VNIC IP addresses, enable Automatically assign public IPv4 address.
- In Add SSH Keys, check Generate a key pair for me and download the private
.keyfile locally. Click Next. - Under Boot volume → custom size slider, leave defaults (50 GB) or increase to 200 GB total across all free-tier instances. > After you attach and connect a volume to an instance, you can use the volume like a regular hard drive. You can also disconnect a volume and attach it to another instance without losing data.
- Click Next to review all the settings you’ve chosen. If everything looks good, click Create. Wait for the instance status to turn to Running (green dot).
Now that the instance is running, let’s setup network connectivity so we can connect to our VM externally.
setup VM connectivity
In this step, we will retrieve a public IP address, and configure OCI’s two-layer firewall configuration (OCI Subnet’s Security List + OS Firewall) to enable inbound traffic for applications listening on the required ports.
The 2-layer firewalls act like this: 1. OCI Subnet’s Security List controls what inbound traffic can reach the VM instance 2. OS Firewall controls what traffic can reach applications running inside the the VM.
retrieve public IP
- Click the instance’s name from the Instances list.
- Copy the Public IP address from the instance details.
setup ingress rules
To let inbound SSH and HTTP traffic reach the VM, we must first add ingress rules for TCP ports 22 and 80 to the security list attached to the public subnet the VM is in.
- In the OCI Console, click the ☰ menu → Networking → Virtual Cloud Networks.
- Select the VCN the your VM instance uses.
- In the VCN’s Resources panel, click Subnets.
- Find and click the instance’s public subnet name.
- On the Subnet details page, in the Security section, click the linked name (e.g., default security list).
- click Security rules
- click Add Ingress Rule (for SSH - Port 22)
- Source CIDR: 0.0.0.0/0 (open to all)
- IP Protocol: TCP
- Source Port Range: Leave blank (all ports)
- Destination Port Range: 22
- Click Add Ingress Rule to save
- Add Ingress Rule for HTTP (Port 80)
- click Add Ingress Rule again
- Source CIDR: 0.0.0.0/0 (open to all)
- IP Protocol: TCP
- Source Port Range: Leave blank
- Destination Port Range: 80
- Click Add Ingress Rule to save
setup OS-level firewall rules
Oracle Linux comes with a firewall (firewalld) that blocks all incoming traffic except SSH (port 22) by default. The OCI Ingress Rule allows traffic to reach the VM instance, but the OS firewall on the instance will drop all non-SSH traffic to applications running inside the the VM.
Different OS distros come with their own firewall implementations. We will look at both Oracle Linux and Ubuntu.
Oracle Linux
We need to enable and start firewalld, then open the firewall ports for web traffic (80) and SSH.
sudo systemctl enable --now firewalld
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=ssh
sudo firewall-cmd --reloadVerify rule by sudo firewall-cmd --list-all.
Ubuntu
For Ubuntu, we need to setup iptables in the following steps:
note: I saw mentioning of using ufw to edit firewall rules in Ubuntu but I shy away from it after reading the official documentation.
Insert SSH-allowing rules before dropping other traffic
Always add rules to permit SSH (and any other essential service) first, then change the default policy. For example:
# 1. Allow SSH sudo iptables -A INPUT -p tcp --dport 22 -m conntrack \ --ctstate NEW,ESTABLISHED -j ACCEPT # 2. Allow loopback and established traffic sudo iptables -A INPUT -i lo -j ACCEPT sudo iptables -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT # 3. Allow HTTP sudo iptables -A INPUT -p tcp --dport 80 -m conntrack \ --ctstate NEW -j ACCEPTUse iptables-apply for automatic rollback Debian/Ubuntu provide
iptables-apply, which tests new rules and reverts them if you don’t confirm within a timeout:sudo apt install netfilter-persistent sudo iptables-apply /etc/iptables/rules.v4It installs your candidate rules and gives you a 10s window to confirm. If you lose connectivity, it automatically rolls back so you aren’t locked out.
Drop everything else
sudo iptables -P INPUT DROP sudo iptables -P FORWARD DROP sudo iptables -P OUTPUT ACCEPTThis way, SSH traffic is explicitly permitted before the DROP policy takes effect.
Persist rules
Once tested and confirmed working, save it by:
sudo netfilter-persistent saveThis writes to/etc/iptables/rules.v4so your policy survives reboots.
Verify on reboot with: sudo iptables -L -v
test connection
For SSH, run ssh -i PATH/TO/PRIVATE_KEY opc@YOUR_VM_PUBLIC_IP (for Oracle Linux) or ubuntu@@YOUR_VM_PUBLIC_IP (for Ubuntu). If you’re connecting to this instance for the first time, you need to accept the fingerprint of the key by typing yes and press Enter.
Now we have full shell access to an Ampere A1 Flex VM instance.
Install dependencies
Next we need to update and install packages to host our Django app in the VM. Oracle Linux uses DNF/YUM instead of APT. If you are using Ubuntu, replace dnf with apt.
sudo dnf update
sudo dnf install -y python3 python3-pip git nginxIn Ubuntu, do an extra install of sudo apt install -y python3-venv as Ubuntu’s packaging separates the pip/venv bits into python3-venv, while Oracle Linux bundles venv inside the main python3 package.
Configure Nginx as reverse proxy
We are now ready to setup Nginx web server.
Enable and start the service:
sudo systemctl enable --now nginxThe service starts a web server that listens on TCP port 80 by default.
Confirm it’s running:
sudo systemctl status nginxTest local access using cURL
This test tries to access the NGINX test page from a terminal session connected via SSH to the instance where NGINX is running. This connection type verifies the service is working and bypasses any firewall rules or OCI ingress security rules.
curl http://localhostWe will see the Nginx default site “Test Page for the HTTP Server on Oracle Linux” or if in Ubuntu: “Welcome to nginx!. If you see this page, the nginx web server is successfully installed and working.”.
Conclusion
We have secured an instance of the popular Arm-based Ampere A1 Flex Shape with maximum CPU cores and RAM in the free tier. We also created a VCN that acts as a canvas for the other OCI resources we will create. Our web server is also up and running. In the next post, we will deploy and run a Django web app to our VM.
Appendix: Errors
Unable to access public IP - The connection has timed out
If curl -i http://127.0.0.1/ works but you still cannot access public IP in browser or via curl, you can test the followings:
Verify Nginx is listening on all interfaces
run:
sudo ss -tulnp | grep ':80'orsudo netstat -tuln | grep 80If
0.0.0.0:80is returned, it means Nginx accepts connections on every network interface, including the public one.Open Port 80 in OCI Networking
Oracle Cloud blocks ingress by default. You’ll need to update Security Lists in your subnet with Ingress rules for port 80. Make sure you following these steps to setup the right rules.
Configure host‐level firewall rules (iptables)
The VM’s own iptables rules might override our ingress rules. Quickly flush them to test:
sudo iptables -L # list rules sudo iptables -F # flush all rules (test only)Then retry:
curl -v http://<PUBLIC_IP>/If this works, it implies that networking and Nginx/Gunicorn setup were already correct, but iptables were blocking.
sudo iptables -Fflushes (clears) all existing firewall rules in the default filter table. That removed any rules that were blocking incoming HTTP (port 80). With no rules in place, Linux defaults to allowing traffic, so your browser will be able to connect to Nginx on port 80.However, we don’t want to run an unprotected server with no firewall rules. To re-enable a safe, persistent firewall, use the steps outlined in this previous section.