Quick guide for setting up a VM instance on Oracle Cloud for self-hosting applications like N8N, Supabase, Chatwoot, or other open-source tools.
Key Focus: The most critical steps are configuring firewall rules (ingress/egress) to allow traffic to reach your applications, and securing a static public IP so you can point your domain's DNS records to a fixed address.
Oracle Cloud has TWO firewall layers that BOTH need configuration:
- Cloud-Level: Security Groups (Oracle Console) - Configure ingress rules
- Server-Level: iptables (Ubuntu instance) - Configure server firewall
Why this matters: Even with correct Security Groups, your applications will be inaccessible due to Ubuntu's default iptables rules blocking traffic. This causes SSL certificate generation to fail and prevents external access to your applications.
This is unique to Oracle Cloud - other providers typically only have one firewall layer.
To increase chances of successful VM provisioning, upgrade to Pay-as-you-go account:
- Navigate to Billing & Cost Management β Upgrade and Manage Payment
- This maintains free tier eligibility while improving resource allocation success
Free Tier Limits:
- ARM A1 Flex: Up to 4 vCPUs and 24 GB RAM (maximum free tier allocation)
- AMD/Intel: VM.Standard.E2.1.Micro (1 vCPU, 1 GB RAM)
Basic Setup:
- Create VM instance with your preferred OS
- Generate SSH key pair and securely store private key - losing this key means losing VM access
- Choose appropriate shape based on your needs
A static public IP is essential for hosting applications as it allows you to point your domain's DNS records to a fixed address.
- β Enable "Automatically assign public IPv4 address" during VM setup
- IP becomes permanent and survives VM restarts
- Recommended approach
- Create VM without public IP
- Navigate to Networking β Reserved Public IPs
- Create reserved IP address
- Manually assign to VM's private IP later
Critical Step: Configure cloud-level firewall rules to allow traffic to your applications.
- Compute β Instances β Your VM
- Click on subnet link under networking details
- Navigate to Security Rules tab
- Click Add Ingress Rules
Add these rules with Source CIDR: 0.0.0.0/0
and IP Protocol: TCP:
Port | Service | Description |
---|---|---|
22 | SSH | Remote access (usually pre-configured) |
80 | HTTP | Web traffic |
443 | HTTPS | Secure web traffic |
3000 | Applications | Custom applications (e.g., Chatwoot) |
5432 | PostgreSQL | Database access (if needed) |
6379 | Redis | Cache access (if needed) |
- Stateless: Leave unchecked (use stateful rules)
- Source Type: CIDR
- Source CIDR:
0.0.0.0/0
(allows access from anywhere) - Destination Port Range: Specific port number
- Ingress: Incoming traffic TO your VM (what you need to configure)
- Egress: Outgoing traffic FROM your VM (usually allowed by default)
# View current INPUT rules (you'll see REJECT rules blocking everything except SSH)
sudo iptables -L INPUT -n -v
# This shows which ports are ACCEPTED vs REJECTED with packet statistics
Add required ports BEFORE the REJECT rule:
# Allow HTTP (port 80)
sudo iptables -I INPUT 4 -p tcp --dport 80 -j ACCEPT
# Allow HTTPS (port 443)
sudo iptables -I INPUT 5 -p tcp --dport 443 -j ACCEPT
# Allow custom application ports (e.g., Chatwoot on 3000)
sudo iptables -I INPUT 6 -p tcp --dport 3000 -j ACCEPT
# Allow PostgreSQL (if needed)
sudo iptables -I INPUT 7 -p tcp --dport 5432 -j ACCEPT
# Allow Redis (if needed)
sudo iptables -I INPUT 8 -p tcp --dport 6379 -j ACCEPT
# Install iptables-persistent to save rules across reboots
sudo apt install iptables-persistent -y
# Save current rules
sudo iptables-save | sudo tee /etc/iptables/rules.v4
# Verify rules are active and see which ports are ACCEPTED
sudo iptables -L INPUT -n -v
Example output after configuration:
Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
996K 1150M ACCEPT 0 -- * * 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED
1 80 ACCEPT 1 -- * * 0.0.0.0/0 0.0.0.0/0
47094 5524K ACCEPT 0 -- lo * 0.0.0.0/0 0.0.0.0/0
17097 1006K ACCEPT 6 -- * * 0.0.0.0/0 0.0.0.0/0 state NEW tcp dpt:22
0 0 ACCEPT 6 -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:80
0 0 ACCEPT 6 -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:443
0 0 ACCEPT 6 -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:3000
276 79229 REJECT 0 -- * * 0.0.0.0/0 0.0.0.0/0 reject-with icmp-host-prohibited
β Good: ACCEPT rules for ports 80, 443, 3000 appear BEFORE the REJECT rule β Bad: If ACCEPT rules appear AFTER REJECT rule, they won't work
- Oracle Cloud Ubuntu instances come with default iptables rules that REJECT all traffic except SSH
- These rules override Security Group settings
- Other cloud providers don't have this issue
- Some application installers (like N8N) automatically fix this, but most (like Chatwoot) don't
# Test if port 80 is accessible externally
sudo python3 -m http.server 80
# Then check http://YOUR_PUBLIC_IP in browser
# Press Ctrl+C to stop test server
Once you have your static public IP, point your domain to it:
- Login to your domain registrar (e.g., Namecheap, GoDaddy, Cloudflare)
- Navigate to DNS management/DNS settings
- Add new A Record:
- Type: A
- Host/Name: @ (for root domain) or subdomain (e.g., app, chat)
- Value/Points to: Your Oracle Cloud static IP
- TTL: 14400 (or default)
Example:
- Host: chat
- Value: 130.162.214.12
- Result: chat.yourdomain.com points to your VM
Once your VM is running and you have the public IP address, connect via SSH:
ssh -i ~/path/to/your-private-key.key ubuntu@YOUR_PUBLIC_IP
Example:
ssh -i ~/Downloads/ssh-key-2025-06-11.key ubuntu@130.162.214.12
After connecting to your VM, run these essential commands:
# Update package repository
sudo apt update
# Install network tools (for netstat and networking diagnostics)
sudo apt install net-tools
Your VM is now ready for application installation!
Problem: Let's Encrypt certificate generation fails with errors like:
- "Error getting validation data"
- "Connection refused"
- "Fetching http://yourdomain.com/.well-known/acme-challenge/ failed"
Cause: Oracle Cloud's iptables blocking HTTP traffic despite correct Security Groups
Solution:
- Verify DNS resolves correctly:
nslookup yourdomain.com
- Check Security Groups allow ports 80 and 443
- Most importantly: Configure server-level iptables (see Step 2 above)
- Test with:
sudo python3 -m http.server 80
and visit http://yourdomain.com
Problem: Application runs internally (e.g., netstat -tlnp | grep :3000
shows it's running) but not accessible externally
Solution:
- Check both Security Groups AND iptables rules
- Add the application's port to both firewall layers
- Restart the application after firewall changes
Problem: Rules added but still not working
Check current rules:
sudo iptables -L INPUT -n -v
Solution: ACCEPT rules must appear BEFORE REJECT rules. If they're after REJECT, they won't work:
# Remove incorrectly placed rule (replace X with line number)
sudo iptables -D INPUT X
# Add rule at correct position (before REJECT rule)
sudo iptables -I INPUT 4 -p tcp --dport 80 -j ACCEPT
# Save changes
sudo iptables-save | sudo tee /etc/iptables/rules.v4
- AWS/Google Cloud: Typically one firewall layer (Security Groups only)
- Oracle Cloud: Two firewall layers (Security Groups + iptables)
- Default behavior: Oracle blocks everything except SSH for security
- Documentation: Oracle's own documentation rarely mentions this requirement
This setup provides a solid foundation for hosting applications on Oracle Cloud's free tier.