oci
Using an oci instance as a source of truth for shared content among computers running Alpine Linux , Linux Mint and android phones amongst family members.
This page contain some notes for creating a AMD VM instance on Oracle Cloud Free Tier running Alpine Linux with lighttpd webserver.
Wasted good many hours trying to get a ampere vm as it came with quad core CPU and huge memory. After trying for multiple days, enabled an amd instance.
Photos from Google Drive were downloaded using google takeout and archived on this.
Prerequisites
- An OCI Account (Always Free tier works perfectly)
- Alpine Linux Cloud image for OCI from cloud↗ page. I used cloud-init image, as i was
not aware of the tiny-cloud↗ at that time.
- Upload the above image to OCI to use as a Custom Image. To upload image, In OCI, go to Storage > Buckets. Select your bucket and click Upload.
Console connection
Oracle Cloud doesn’t accept ED25519 type SSH keys for Console Connections. Oracle prefers RSA keys. Generate an RSA Key:
ssh-keygen -t rsa -b 4096 -f ~/.ssh/oracle_rsa_key
Add SSH Key via Instance Metadata (Easiest)
SSH key needs to be added so that login using builtin account alpine works.
This works if your instance is running and cloud-init is working: Steps:
Go to Compute → Instances Click your instance name Click Edit (top of the page) Scroll down to SSH keys section Click Add SSH keys Either: Paste public keys: Paste your new RSA public key Choose public key files: Upload your .pub file Click Save changes
Instance Configuration
Instance Creation
- Navigate to Compute Instances - Menu → Compute → Instances → Create Instance
- Configure basic details - Name: alpine-x86, Compartment: root, Availability Domain: selected
- Select custom Alpine Linux image - Image and Shape → Change Image → Custom Images tab → Selected uploaded Alpine cloud-init image
- Choose AMD shape - Change Shape → AMD → VM.Standard.E2.1.Micro (1 OCPU, 1GB RAM, Free Tier eligible)
- Configure networking - Selected “Create new virtual cloud network” option
- Create public subnet - “Create new public subnet” with CIDR 10.0.0.0/24. Note: The word public must be on the subnet name for this to work.
- Enable public IPv4 address - Checked “Automatically assign public IPv4 address” (requires public subnet)
- Add SSH key - Uploaded/pasted RSA public key Note: ED25519 not supported for console connections
- Create instance - Waited 2-3 minutes for provisioning.
Network Configuration
- Added ingress rule for SSH (port 22) - VCN Security List: 0.0.0.0/0, TCP, port 22
Instance Access
Connected via SSH -
ssh -i ~/.ssh/key alpine@oci
Initial system check - Verified disk (48GB), RAM (1GB), user (alpine)
OS Configuration
swap
Added a swap file to avoid memory issues
doas dd if=/dev/zero of=/swapfile bs=1M count=2048
doas chmod 600 /swapfile
doas mkswap /swapfile
doas swapon /swapfile
echo "/swapfile none swap sw 0 0" | doas tee -a /etc/fstab
cat /etc/fstab
To force a memory reload and remove ghost usage..
doas swapoff -a && doas swapon -a
lighttpd
Used lighttpd for serving files using WebDAV for orgzly on android running on Note6Pro and also to quickly do a oneway sync of Non_fiction books by rcx .
Added ingress rule for WebDAV (port 8080) - VCN Security List: 0.0.0.0/0, TCP, port 8080
To manage the permission on /var/www/webdav/agenda folder, the acf package was installed.
setfacl -R -m g:lighttpd:rwX /var/www/webdav/agenda
setfacl -R -d -m g:lighttpd:rwX /var/www/webdav/agenda
The working of acl can’t be seen in normal ls -l..
admin@alpine-x86 /v/w/w/agenda> ls -l todo.org
-rw-rw-r-- 1 lighttpd lighttpd 3417 Feb 9 20:33 todo.org
admin@alpine-x86 /v/w/w/agenda> getfacl todo.org
# file: todo.org
# owner: lighttpd
# group: lighttpd
user::rw-
group::r--
group:lighttpd:rw-
mask::rw-
other::r--
admin@alpine-x86 /v/w/w/agenda> getfacl /var/www/webdav/agenda
getfacl: Removing leading '/' from absolute path names
# file: var/www/webdav/agenda
# owner: syncthing
# group: lighttpd
# flags: -s-
user::rwx
group::rwx
group:lighttpd:rwx
mask::rwx
other::r-x
default:user::rwx
default:group::rwx
default:group:lighttpd:rwx
default:mask::rwx
default:other::r-x
The alternate approach of adding umask 002 in /etc/conf.d/lighttpd did not work too.
syncthing
syncthing page has all configuration information. Information relevant to OCI are kept only here..
Add ingress rule for syncthing (port 22001) - VCN Security List: 0.0.0.0/0, TCP, port 22001
To see if Syncthing is approaching your 150MiB limit during a sync:
watch "ps -o pid,user,rss,args | grep syncthing"
filebrowser
Instead of webdav, filebrowser application has been installed to make upload/download easier to manage.
Configured the service to run as lighttpd user to avoid ownership conflicts as the same content is shared through both this and lighttpd as WebDAV share.
admin@alpine-x86 ~> cat /etc/init.d/filebrowser
#!/sbin/openrc-run
description="filebrowser server"
command="/usr/bin/filebrowser"
command_args="$command_args"
command_background=true
#command_user="filebrowser:filebrowser"
command_user="lighttpd:lighttpd"
pidfile="/run/$RC_SVCNAME.pid"
depend() {
need net
after firewall
}
admin@alpine-x86 ~> cat /etc/conf.d/filebrowser
command_args="--database /var/lib/filebrowser/database.db --log /var/log/filebrowser.log --root /data/docs/Books"
The ownership was changed to the same group as lighttpd to avoid permission issues at the OS level and to avoid conflicts with syncthing .
doas chown -R lighttpd:lighttpd /var/lib/filebrowser/data
Due to locking reasons, the service needs to be stopped before making changes to the database.
admin@alpine-x86 ~> doas filebrowser config cat -d /var/lib/filebrowser/database.db
2026/02/20 16:40:51 timeout
admin@alpine-x86 ~ [0|1]> doas filebrowser config cat -d /var/lib/filebrowser/database.db
Sign up: false
Create User Dir: false
Auth method: json
Shell:
Branding:
Name:
Files override: /var/lib/filebrowser/data
Disable external links: false
Disable used disk percentage graph: false
Color:
Theme: dark
Server:
Log: stdout
Port: 8081
Base URL:
Root: .
Socket:
Address: 0.0.0.0
TLS Cert:
TLS Key:
Exec Enabled: false
Defaults:
Scope: .
Locale: en
View mode: list
Single Click: false
Commands:
Sorting:
By: name
Asc: false
Permissions:
Admin: false
Execute: true
Create: true
Rename: true
Modify: true
Delete: true
Share: true
Download: true
Auther configuration (raw):
{
"recaptcha": null
}
Added the following packages, i.e mailcap and shared-mime-info. Still preview eye icon not appearing.
Rules to manage user access
To allow a user to use specific folder only, this 3-step rule must be followed to avoid ghost folders:
doas filebrowser -d /var/lib/filebrowser/database.db rules add --username user1 --regexp --type allow --path "^/myhome$"
doas filebrowser -d /var/lib/filebrowser/database.db rules add --username user1 --regexp --type disallow --path "^/myhome/.*"
doas filebrowser -d /var/lib/filebrowser/database.db rules add --username user1 --regexp --type allow --path "^/myhome/user1(/.*)?$"
to verify exactly what user1 can see without logging in, use this command:
doas filebrowser -d /var/lib/filebrowser/database.db rules ls --username user1
Tailscale
Since Sastra university Wifi doesn’t allow using freeddns.org, i’ve configured a tailscale funnel.
Using Tailscale to act as a “pass-through” (Funnel) to existing secure server. Using cloudfared either leads to ugly URL’s due to unregistered option or requires a tld.
Based on below output, Gemini says that lighttpd is configured to bind to IPv4 (0.0.0.0:443). Tailscale binds to IPv6 ([::]:443) on the tailscale0 interface to avoid port collisions. I’m still unsure..
doas netstat -tpln | grep :443
tcp 0 0 0.0.0.0:443 0.0.0.0:* LISTEN 4894/lighttpd
tcp 0 0 fd7a:115c:a1e0::2401:7dac:443 :::* LISTEN 5633/tailscaled
Three steps to configure:
- Installed the package and started the service using doas.
- Authenticate using below command:It will give you a link. Paste that into your browser, logged in using Google to congigure server as part of your “Tailnet.”
doas tailscale up - Because Lighttpd is using HTTPS on port 443, use the https+insecure
prefix. This tells Tailscale not to worry about verifying the
certificate locally as we are using Encrypt/self-signed for
freeddns.org and not the Tailscale name.
doas tailscale funnel --bg https+insecure://localhost:443 doas tailscale funnel status doas tailscale status
This is the current status of the funnel.
admin@alpine-x86 ~> doas tailscale funnel status
In future,to shut off the public internet or this can be done on the site too..
doas tailscale funnel reset
Errors due to resolvconf
If tailscale status, gave the following error, need to remove /etc/resolv.conf as tailscale autocreates the file from /etc/resolvconf.conf
# Health check:
# - Tailscale failed to set the DNS configuration of your device: running /usr/libexec/tailscale/resolvconf -m 0 -x -a tailscale: resolvconf: signature mismatch: /etc/resolv.conf
resolvconf: run `resolvconf -u` to update
# - running /usr/libexec/tailscale/resolvconf -m 0 -x -a tailscale: resolvconf: signature mismatch: /etc/resolv.conf
resolvconf: run `resolvconf -u` to update
sshguard
Added sshguard for protection against ssh attacks.
fail2ban
Added fail2ban for protection to lighttpd and filebrowser.
The below default jail for lighttpd with filter lighttpd-auth.conf was not working.
admin@alpine-x86 ~> cat /etc/fail2ban/jail.d/lighttpd.conf
admin@alpine-x86 ~> cat /etc/fail2ban/filter.d/lighttpd-auth.conf
The following was added for lighttpd and it works as per Verification methods given below.
admin@alpine-x86 /e/f/filter.d> cat /etc/fail2ban/jail.d/lighttpd-401.conf
admin@alpine-x86 /e/f/filter.d> cat /etc/fail2ban/filter.d/lighttpd-401.conf
The jail and associated filter config given on filebrowser site did not work. Modified as below and this works..
admin@alpine-x86 ~> cat /etc/fail2ban/jail.d/filebrowser.conf
admin@alpine-x86 ~> cat /etc/fail2ban/filter.d/filebrowser.conf
Verification
To verify if fail2ban filters are effective, the following command can be modified and issued.
doas fail2ban-regex /var/log/lighttpd/access.log /etc/fail2ban/filter.d/lighttpd-401.conf
To verify the status of jail
admin@alpine-x86 /e/f/filter.d> doas fail2ban-client status lighttpd-401
Status for the jail: lighttpd-401
|- Filter
| |- Currently failed: 0
| |- Total failed: 78
| `- File list: /var/log/lighttpd/access.log
`- Actions
|- Currently banned: 0
|- Total banned: 11
`- Banned IP list:
To view the current fail2ban config
doas fail2ban-client -d
whitelist
To unban an ip by fail2ban,
doas fail2ban-client unban 103.163.XX.45
For fail2ban, in the [DEFAULT] section, Added my ip range in the configuration file.
admin@alpine-x86 /e/f/filter.d> cat /etc/fail2ban/jail.conf|grep ignoreip
ignoreip = 127.0.0.1/8 ::1 103.163.XX.0/24
To verify the working of whitelist in fail2ban
admin@alpine-x86 /e/f/filter.d> doas fail2ban-client get lighttpd-401 ignoreip
These IP addresses/networks are ignored:
|- 127.0.0.0/8
|- 103.163.XX.0/24
`- ::1
For sshguard, the procedure to whitelist requires adding files and adding a reference to it.
admin@alpine-x86 /e/f/filter.d> cat /etc/sshguard.whitelist
103.163.XX.0/24
admin@alpine-x86 /e/f/filter.d> cat /etc/sshguard.conf
...
WHITELIST_FILE=/etc/sshguard.whitelist
To verify if whitelist is working for sshguard, restart the service and check.
admin@alpine-x86 /e/f/filter.d [1]> ps aux | grep sshguard
12621 root 0:00 supervise-daemon sshguard --start --stderr-logger logger -t sshguard -p daemon.err >/dev/null 2>&1 --pidfile /run/sshguard.pid --respawn-delay 2 --respawn-max 5 --respawn-period 1800 /usr/sbin/sshguard --
12622 root 0:00 {sshguard} /bin/sh /usr/sbin/sshguard
12624 root 0:00 logger -t sshguard -p daemon.err
12629 root 0:00 {sshguard} /bin/sh /usr/sbin/sshguard
12632 root 0:00 {sshguard} /bin/sh /usr/sbin/sshguard
12633 root 0:00 /usr/libexec/sshg-blocker -a 30 -p 600 -s 3600 -w /etc/sshguard.whitelist
17466 admin 0:00 grep --color=auto sshguard
iptables
The effect of both sshguard and fail2ban can be see in the iptables
doas iptables -L -n -v
To test a firewall ban due to fail2ban, the following commands can be issued:
admin@alpine-x86 /e/f/filter.d> doas fail2ban-client set lighttpd-401 banip 204.76.203.8
1
admin@alpine-x86 /e/f/filter.d> doas iptables -L -n -v
Chain INPUT (policy ACCEPT 3098K packets, 2542M bytes)
pkts bytes target prot opt in out source destination
137 11820 f2b-lighttpd-401 tcp -- * * 0.0.0.0/0 0.0.0.0/0
3073K 2524M sshguard all -- * * 0.0.0.0/0 0.0.0.0/0
Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
Chain f2b-lighttpd-401 (1 references)
pkts bytes target prot opt in out source destination
0 0 REJECT all -- * * 204.76.203.8 0.0.0.0/0 reject-with icmp-port-unreachable
137 11820 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0
Chain sshguard (1 references)
pkts bytes target prot opt in out source destination
Timezone Change
The default timezone from cloud-init image is GMT.
setup-timezone -z Asia/Kolkata
crontab
admin@alpine-x86 ~> doas crontab -l |tail -2
*/30 * * * * /usr/local/bin/random-stress.sh >/dev/null 2>&1
SSL certification
In the beginning the site was running based authentication built into the lighttpd. As part of moving to https, used certbot tool with letsencrypt.
certbot requires port 80 to be open, opened the vcn for ports 80,443.
admin@alpine-x86 ~> doas certbot certonly --webroot -w /var/www/localhost/htdocs -d prabu.freeddns.org
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Requesting a certificate for prabu.freeddns.org
Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/prabu.freeddns.org/fullchain.pem
Key is saved at: /etc/letsencrypt/live/prabu.freeddns.org/privkey.pem
This certificate expires on 2026-05-22.
These files will be updated when the certificate renews.
Oracle Reclamation policy
Oracle’s “Always Free” reclamation is assumed to be based on a 7-day rolling average utilization rate of 20% threshold for both cpu and memory.
To check current cpu utilization run this on your OCI instance:
$ top -n 1 -b | head -n 20
Troubleshooting
Intrusion handling
If netstat shows unknown sessions, here’s the investigation step to identify the software that initiated the session.
admin@alpine-x86 ~> netstat
Active Internet connections (w/o servers)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 alpine-x86.public.alpinevcn.oraclevcn.com:53916 85.121.244.57:22067 ESTABLISHED
admin@alpine-x86 ~> doas netstat -tpn | grep "85.121.244.57"
tcp 0 0 10.0.0.158:53916 85.121.244.57:22067 ESTABLISHED 14956/syncthing
Verify services after starting
To check whether a service is listening on a configured port:
admin@alpine-x86 ~> doas netstat -tulpn | grep 8081
tcp 0 0 :::8081 :::* LISTEN 16306/filebrowser
To check which user is running a service
ps -o pid,user,args | grep filebrowser
© Prabu Anand K 2020-2026