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.

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

Network Configuration

Instance Access

Connected via SSH -

ssh -i ~/.ssh/key alpine@140.245.241.34

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 .

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..

prabu@alpine-x86 /v/w/w/agenda> ls -l todo.org
-rw-rw-r--    1 lighttpd lighttpd      3417 Feb  9 20:33 todo.org
prabu@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--
prabu@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.

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.

prabu@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
}
prabu@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.

prabu@alpine-x86 ~> doas filebrowser config cat -d /var/lib/filebrowser/database.db
2026/02/20 16:40:51 timeout
prabu@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:

This is the current status of the funnel.

prabu@alpine-x86 ~> doas tailscale funnel status

# Funnel on:
#     - https://alpine-x86.taila5a6bd.ts.net

https://alpine-x86.taila5a6bd.ts.net (Funnel on)
|-- / proxy https+insecure://localhost:443

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.

prabu@alpine-x86 ~> cat /etc/fail2ban/jail.d/lighttpd.conf
prabu@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.

prabu@alpine-x86 /e/f/filter.d> cat /etc/fail2ban/jail.d/lighttpd-401.conf
prabu@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..

prabu@alpine-x86 ~> cat /etc/fail2ban/jail.d/filebrowser.conf
prabu@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

prabu@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.248.45

For fail2ban, in the [DEFAULT] section, Added my ip range in the configuration file.

prabu@alpine-x86 /e/f/filter.d> cat /etc/fail2ban/jail.conf|grep ignoreip
ignoreip = 127.0.0.1/8 ::1 103.163.248.0/24

To verify the working of whitelist in fail2ban

prabu@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.248.0/24
`- ::1

For sshguard, the procedure to whitelist requires adding files and adding a reference to it.

prabu@alpine-x86 /e/f/filter.d> cat /etc/sshguard.whitelist
103.163.248.0/24
prabu@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.

prabu@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 prabu     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:

prabu@alpine-x86 /e/f/filter.d> doas fail2ban-client set lighttpd-401 banip 204.76.203.8
1
prabu@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

  prabu@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.

prabu@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.

prabu@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

prabu@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:

  prabu@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