Skip to content

Troubleshooting

Organized by symptom. Find your symptom, work through the checks in order, skip to the next symptom if the current one doesn't apply.

Can't SSH

Symptom: ssh: connect to host <ip> port 22: Connection timed out or Permission denied (publickey).

Timeout — you can't even reach port 22:

  1. Is the instance actually running? OCI console → Compute → Instances → check state = Running.
  2. Is the public IP right? OCI console → Instance → Networking → Primary VNIC → Public IPv4 address.
  3. OCI security list allows port 22? Networking → VCN → Security Lists → default → Ingress Rules. Should have a rule allowing TCP 22 from 0.0.0.0/0 (or your specific IP).
  4. Route table has default route to Internet Gateway? Networking → VCN → Route Tables → the one attached to your subnet. Should have 0.0.0.0/0 → Internet Gateway.
  5. Instance firewall allows port 22? SSH via the OCI serial console (Instance → Console Connections → Launch Cloud Shell Connection or serial console) to check iptables -L / nft list ruleset.

Permission denied (publickey):

  1. Right SSH key on your local machine? ssh -i ~/.ssh/oci_lab ubuntu@<ip> -v — the -v flag shows which key is being tried.
  2. Right SSH key in the instance's authorized_keys? Log in via OCI's serial console and check cat /home/ubuntu/.ssh/authorized_keys.
  3. Right username? For Ubuntu images, it's ubuntu. For Oracle Linux images, it's opc. For custom images, whatever you set.
  4. Permissions on the private key? On your local machine: chmod 600 ~/.ssh/oci_lab. SSH refuses to use keys with too-open permissions.

Caddy TLS fails

Symptom: sudo journalctl -u caddy shows something like no such host, connection refused, or too many failed authorizations.

  1. DNS resolves to the right IP?

    dig +short lab.example.com @1.1.1.1
    
    Should return your instance's public IP. If not, DNS isn't set up right — see DNS with Cloudflare.

  2. Port 443 is open at OCI? VCN Security List → Ingress → TCP 443 allowed from 0.0.0.0/0.

  3. Port 443 is open in the instance firewall?

    sudo iptables -L INPUT -n | grep -E '(80|443)'
    # or
    sudo nft list ruleset | grep -E '(80|443)'
    

  4. Port 80 also open? Let's Encrypt's HTTP-01 challenge (which Caddy tries as a fallback) needs port 80. Even if you want to force HTTPS-only, port 80 needs to be reachable for the challenge.

  5. DNS record is gray-cloud (DNS only), not orange-cloud (Proxied)? Cloudflare orange-cloud terminates TLS at Cloudflare's edge and Caddy can't answer the tls-alpn-01 challenge. See DNS gotcha.

  6. Let's Encrypt rate-limited you? Look for too many failed authorizations or rate limited in the journal. Wait ~1 hour and try again. To avoid this during debugging, temporarily use the staging endpoint:

    {
        email you@example.com
        acme_ca https://acme-staging-v02.api.letsencrypt.org/directory
    }
    
    Verify staging works, then remove the acme_ca line and reload for real certs.

  7. Caddy actually listening on 443?

    sudo ss -tlnp | grep :443
    
    Should show caddy. If nothing's on 443, systemctl status caddy — something's wrong with the service.

DNS not resolving

Symptom: dig lab.example.com returns nothing, or curl says Could not resolve host.

  1. Cloudflare is authoritative for the domain?

    dig NS example.com
    
    Should return two .ns.cloudflare.com nameservers. If not, your registrar hasn't propagated the nameserver change yet — wait up to 48 hours (usually much less).

  2. A record exists in Cloudflare? DNS → Records → check for lab (or whatever subdomain) with type A pointing at your IP.

  3. Waiting on caching? If you previously queried before creating the record, negative caching (NXDOMAIN) can persist for minutes. Try:

    dig +short lab.example.com @1.1.1.1
    dig +short lab.example.com @8.8.8.8
    
    Direct queries to public resolvers skip your local cache.

  4. Right hostname? Typos in Caddyfile vs. DNS records happen. Copy from one to the other.

OCI shows charges I didn't expect

Symptom: your budget alert fired or your Cost Analysis shows non-zero.

Walk the staying-free checklist. Common culprits:

  • NAT Gateway from the VCN Wizard, never deleted
  • Flexible Load Balancer created by accident
  • Extra Reserved Public IPs not attached to instances
  • Boot volumes >47 GB × 3 instances >141 GB, adding block volumes pushes over 200 GB total
  • Bandwidth over 10 TB/month — check Usage Reports

ARM Ampere "out of host capacity"

See Provision the ARM Ampere → Capacity constraints. Not really a bug, just the free-tier reality.

Instance is running but unreachable, and it was reachable before

Something changed. Recent changes to check, most-recent first:

  1. You edited nftables/iptables and now port 22 doesn't work. OCI console → Instance → Console Connections → serial console. Fix the rules from the serial console.

  2. OCI security list was edited. Check ingress rules are still there.

  3. Boot disk is full. Serial console → df -h. If root is at 100%, SSH can start but sshd may fail to create session files. Free space or resize the boot volume.

  4. Reboot in progress. Check the instance status in the OCI console — if it's showing "Provisioning" or "Stopping", wait.

Caddy reloaded but old content still served

  1. Browser cached the old content. Try a hard refresh (Ctrl+F5 or Cmd+Shift+R) or an incognito window.

  2. Caddy served-from-cache. Caddy has a small cache for static files. Restart instead of reloading:

    sudo systemctl restart caddy
    

  3. Reload failed silently. Check the journal:

    sudo journalctl -u caddy -n 30
    
    Look for a Caddyfile parse error. Caddy refuses to load a broken config and keeps serving the old one.

  4. You wrote to the wrong directory. sudo cat /etc/caddy/Caddyfile — confirm the root directive points where you think.

The instance was terminated and I need it back

You can restore a terminated instance from its boot volume if the boot volume wasn't also deleted (Terminate has a "Permanently delete the attached boot volume" checkbox — leave it unchecked to keep the option open).

  1. StorageBoot Volumes → find the one for the terminated instance.
  2. … menuCreate Instance → configure a new instance using this boot volume.

If the boot volume was also deleted, restore from a manual backup if you had one, or start over. This is why the staying-free checklist suggests periodic backup — Object Storage is free for the first 20 GB and boot volume backups compress well.

Still stuck?