Skip to content

Instantly share code, notes, and snippets.

@thde
Last active September 17, 2024 03:27
Show Gist options
  • Save thde/5312a42665c5c901aef4 to your computer and use it in GitHub Desktop.
Save thde/5312a42665c5c901aef4 to your computer and use it in GitHub Desktop.
A script to install alpine linux on a dedicated server. Tested on Hetzner, Kimsufi / OVH
#!/bin/sh
set -ex
PATH=/bin:/sbin:/usr/bin:/usr/sbin
KEYMAP="us us"
HOST=alpine
USER=anon
ROOT_FS=ext4
BOOT_FS=ext4
FEATURES="ata base ide scsi usb virtio $ROOT_FS"
MODULES="sd-mod,usb-storage,$ROOT_FS"
REL=3.6
MIRROR=http://dl-cdn.alpinelinux.org/alpine
REPO=$MIRROR/v$REL/main
APKV=2.7.2-r0
DEV=/dev/sdb
ROOT_DEV=${DEV}2
BOOT_DEV=${DEV}1
ROOT=/mnt
BOOT=/mnt/boot
ARCH=$(uname -m)
sgdisk -Z $DEV
sgdisk -n 1:0:+512M $DEV
sgdisk -t 1:8300 $DEV
sgdisk -c 1:boot $DEV
sgdisk -n 2:0:+20G $DEV
sgdisk -t 2:8300 $DEV
sgdisk -c 2:root $DEV
sgdisk -A 1:set:2 $DEV
mkfs.$BOOT_FS -m 0 -q -L boot $BOOT_DEV
mkfs.$ROOT_FS -q -L root $ROOT_DEV
mount $ROOT_DEV $ROOT
mkdir $BOOT
mount $BOOT_DEV $BOOT
curl -s $MIRROR/v$REL/main/$ARCH/apk-tools-static-${APKV}.apk | tar xz
./sbin/apk.static --repository $REPO --update-cache --allow-untrusted --root $ROOT --initdb add alpine-base syslinux dhcpcd
cat << EOF > $ROOT/etc/fstab
$ROOT_DEV / $ROOT_FS defaults,noatime 0 0
$BOOT_DEV /boot $BOOT_FS defaults 0 2
EOF
echo $REPO > $ROOT/etc/apk/repositories
cat /etc/resolv.conf > $ROOT/etc/resolv.conf
cat << EOF > $ROOT/etc/update-extlinux.conf
overwrite=1
vesa_menu=0
default_kernel_opts="quiet"
modules=$MODULES
root=$ROOT_DEV
verbose=0
hidden=1
timeout=1
default=grsec
serial_port=
serial_baud=115200
xen_opts=dom0_mem=256M
password=''
EOF
cat << EOF > $ROOT/etc/network/interfaces
auto lo
iface lo inet loopback
auto eth0
iface eth0 inet dhcp
hostname $HOST
EOF
mount --bind /proc $ROOT/proc
mount --bind /dev $ROOT/dev
mount --bind /sys $ROOT/sys
chroot $ROOT /bin/sh -x << CHROOT
apk update
apk add openssh
setup-hostname -n $HOST
rc-update -q add devfs sysinit
rc-update -q add dmesg sysinit
rc-update -q add mdev sysinit
rc-update -q add hwdrivers sysinit
rc-update -q add hwclock boot
rc-update -q add modules boot
rc-update -q add sysctl boot
rc-update -q add hostname boot
rc-update -q add bootmisc boot
rc-update -q add syslog boot
rc-update -q add networking boot
rc-update -q add urandom boot
rc-update -q add dhcpcd boot
rc-update -q add mount-ro shutdown
rc-update -q add killprocs shutdown
rc-update -q add savecache shutdown
rc-update -q add acpid default
rc-update -q add crond default
rc-update -q add sshd default
echo features=\""$FEATURES"\" > /etc/mkinitfs/mkinitfs.conf
apk add linux-grsec
extlinux -i /boot
dd bs=440 conv=notrunc count=1 if=/usr/share/syslinux/gptmbr.bin of=$DEV
CHROOT
chroot $ROOT passwd
chroot $ROOT adduser -s /bin/ash -D $USER
chroot $ROOT passwd $USER
umount $ROOT/proc
umount $ROOT/dev
umount $ROOT/sys
umount $BOOT
umount $ROOT
@vir2l
Copy link

vir2l commented Oct 30, 2017

Hey Demian,
this is a great peace of work, that helped me a lot getting alpine running. But as i am a complete alpine beginner (just converted from debian) i've got some misunderstandings according to this script. Maybe you can get a bit into details for some lines:

5 PATH=/bin:/sbin:/usr/bin:/usr/sbin seems to be unused right?

6 KEYMAP="us us" unused?

11 FEATURES="ata base ide scsi usb virtio $ROOT_FS" I have added "raid" here to get my system booting. What exactly is that? Kernel modules? It has something to do with the RamFS. Can you explain that a little better?

12 MODULES="sd-mod,usb-storage,$ROOT_FS" Curiously, I don't have to specify any raid modules for the kernel here...

44 ./sbin/apk.static --repository $REPO --update-cache --allow-untrusted --root $ROOT --initdb add alpine-base syslinux dhcpcd At this point i added "mdadm" to be right there... or what do you think?

49 cat << EOF > $ROOT/etc/update-extlinux.conf Since I come from Debian Syslinux/Extlinux is totally new to me.

84 rc-update -q add devfs sysinit Where should I put mdadm? sysinit or boot?

107 echo features=\""$FEATURES"\" > /etc/mkinitfs/mkinitfs.conf Can you explain me the boot process step by step? After BIOS/UEFI has passed to the bootloader (syslinux)... initramfs -> then kernel from / partition? I ask because syslinux knows where to find the kernel... or does syslinux need the initramfs?

It would be great if you could spent some time to give me some support and answer my questions. If you wish, I can post you my Raid-Version of your script. Its working pretty stable, although I do not understand why ;-)

Greetings
David

@vinay212
Copy link

vinay212 commented Jun 7, 2018

I have got a tar file in root of alpine ISO. During the installation of Alpine i want to extract the folder to /user/sbin. Where should i add the tar command to extract the tar file ?

@steveb999
Copy link

How do you use this script ?

@o-be-one
Copy link

Update for Alpine Linux 3.9, adapted and tested on Kimsufi (installed from NetBoot: rescue):

#!/bin/sh

set -ex

PATH=/bin:/sbin:/usr/bin:/usr/sbin
KEYMAP="us us"
HOST=alpine1
USER=obeone
ROOT_FS=ext4
BOOT_FS=ext4
FEATURES="ata base ide scsi usb virtio $ROOT_FS network"
MODULES="sd-mod,usb-storage,$ROOT_FS,e1000e"
REL=3.9
MIRROR=http://dl-cdn.alpinelinux.org/alpine
REPO=$MIRROR/v$REL/main
APKV=2.10.3-r1
DEV=/dev/sda
ROOT_DEV=${DEV}2
BOOT_DEV=${DEV}1
ROOT=/mnt
BOOT=/mnt/boot
ARCH=$(uname -m)

sgdisk -Z $DEV
sgdisk -n 1:0:+512M $DEV
sgdisk -t 1:8300 $DEV
sgdisk -c 1:boot $DEV
sgdisk -n 2:0:+20G $DEV
sgdisk -t 2:8300 $DEV
sgdisk -c 2:root $DEV
sgdisk -A 1:set:2 $DEV

mkfs.$BOOT_FS -m 0 -q -L boot $BOOT_DEV
mkfs.$ROOT_FS -q -L root $ROOT_DEV
mount $ROOT_DEV $ROOT
mkdir $BOOT
mount $BOOT_DEV $BOOT

curl -s $MIRROR/v$REL/main/$ARCH/apk-tools-static-${APKV}.apk | tar xz
./sbin/apk.static --repository $REPO --update-cache --allow-untrusted --root $ROOT --initdb add alpine-base syslinux dhcpcd

cat << EOF > $ROOT/etc/fstab
$ROOT_DEV / $ROOT_FS defaults,noatime 0 0
$BOOT_DEV /boot $BOOT_FS defaults 0 2
EOF
echo $REPO > $ROOT/etc/apk/repositories

cat << EOF > $ROOT/etc/resolv.conf
nameserver 8.8.8.8
nameserver 8.8.4.4
EOF

cat << EOF > $ROOT/etc/update-extlinux.conf
overwrite=1
vesa_menu=0
default_kernel_opts="quiet"
modules=$MODULES
root=$ROOT_DEV
verbose=0
hidden=1
timeout=1
default=grsec
serial_port=
serial_baud=115200
xen_opts=dom0_mem=256M
password=''
EOF

cat << EOF > $ROOT/etc/network/interfaces
auto lo
iface lo inet loopback
auto eth0
iface eth0 inet static
  address XXX.XXX.XXX.YYY
  netmask 255.255.255.255
  broadcast XXX.XXX.XXX.255
  gateway XXX.XXX.XXX.254
  network XXX.XXX.XXX.0
EOF

mount --bind /proc $ROOT/proc
mount --bind /dev $ROOT/dev
mount --bind /sys $ROOT/sys

chroot $ROOT /bin/sh -x << CHROOT
apk update
apk add openssh
setup-hostname -n $HOST
rc-update -q add devfs sysinit
rc-update -q add dmesg sysinit
rc-update -q add mdev sysinit
rc-update -q add hwdrivers sysinit
rc-update -q add hwclock boot
rc-update -q add modules boot
rc-update -q add sysctl boot
rc-update -q add hostname boot
rc-update -q add bootmisc boot
rc-update -q add syslog boot
rc-update -q add networking boot
rc-update -q add urandom boot
rc-update -q add dhcpcd boot
rc-update -q add mount-ro shutdown
rc-update -q add killprocs shutdown
rc-update -q add savecache shutdown
rc-update -q add acpid default
rc-update -q add crond default
rc-update -q add sshd default
echo features=\""$FEATURES"\" > /etc/mkinitfs/mkinitfs.conf
apk add linux-vanilla
extlinux -i /boot
dd bs=440 conv=notrunc count=1 if=/usr/share/syslinux/gptmbr.bin of=$DEV
CHROOT

chroot $ROOT passwd
chroot $ROOT adduser -s /bin/ash -D $USER
chroot $ROOT passwd $USER

umount $ROOT/proc
umount $ROOT/dev
umount $ROOT/sys
umount $BOOT
umount $ROOT

@thde
Copy link
Author

thde commented Mar 14, 2019

How do you use this script ?

Boot up the machine in rescue boot mode and run the script. You have to correct the variables beforehand.

@thde
Copy link
Author

thde commented Mar 14, 2019

Update for Alpine Linux 3.9, adapted and tested on Kimsufi (installed from NetBoot: rescue):

Thanks for posting it! :)

@stacyharper
Copy link

I got a problem after updating my Alpine and rebooting the machine. It seems my networks config just isn't compatible with the Kimsufi network anymore.

Any one got the same problems ?

@Mon-ius
Copy link

Mon-ius commented Feb 16, 2024

linux-vanilla not existed anymore. Any update for AlpineLinux 3.19 ?

@thde
Copy link
Author

thde commented Feb 16, 2024

Should be linux-lts since 3.11

linux-vanilla has been removed. Install linux-lts when upgrading.

https://alpinelinux.org/posts/Alpine-3.11.0-released.html

@Mon-ius
Copy link

Mon-ius commented Feb 17, 2024

But it happens a wierd thing that when I perform the install inside a live system, the first reboot success, the second shoot failed. I am not familiar with alpine so far, wonder if you can help to take a look;

#!/bin/dash

_OS=alpine
_ARC=$(dpkg --print-architecture)
_MIRROR=http://images.linuxcontainers.org
_FILTERED_INDEX=$(curl -fsSL "${_MIRROR}/meta/1.0/index-system" | grep -v edge)
_INDEX=$(echo "$_FILTERED_INDEX" | awk -F';' -v os="$_OS" -v arch="$_ARC" '$1==os && $3==arch {print $NF}' | tail -1)
_TARGET="${_MIRROR}/${_INDEX}rootfs.tar.xz"

XUSER=m0nius
HOST=computing-alpine
PEM="ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBUG8QsUdArpYbyQPgXIYISf6G2q9t6s+qxP5K8Vafc6"
FEATURES="ata base ide scsi usb virtio ext4 network"
MODULES="sd-mod,usb-storage,ext4,e1000e"

ROOT=$(findmnt -no SOURCE /)
ROOT_DEV="/dev/$(lsblk -ndo pkname "$ROOT")"
ROOTFS_MNT=/mnt.$_ARC
BOOT_LIB="/usr/share/syslinux"

sudo mkdir -p "$ROOTFS_MNT"/boot
curl -fsSL "$_TARGET" | sudo tar -C "$ROOTFS_MNT" -xJ

IFACE=$(ip route get 8.8.8.8 | sed -n 's/.*dev \([^\ ]*\).*/\1/p' | head -n 1)
_IPV4=$(ip addr show dev "$IFACE" | awk '/inet /{print $2}' | cut -d' ' -f2)
_IPv6=$(ip addr show dev "$IFACE" | awk '/inet6 /{print $2}' | cut -d' ' -f2)
GATEWAY=$(ip route show default | awk '/default/ {print $3}')

cat <<EOF | sudo tee "$ROOTFS_MNT"/etc/network/interfaces
auto lo
iface lo inet loopback

auto $IFACE
iface $IFACE inet static
    address $_IPV4
    gateway $GATEWAY
EOF

cat <<EOF | sudo tee "$ROOTFS_MNT"/etc/resolv.conf
nameserver 1.1.1.1
EOF

find / \( ! -path '/dev/*' -and ! -path '/proc/*' -and ! -path '/sys/*' -and ! -path '/selinux/*' -and ! -path "$ROOTFS_MNT/*" \) -delete 2>/dev/null || true

"$ROOTFS_MNT/lib/ld-musl-x86_64.so.1" "$ROOTFS_MNT/bin/busybox" cp -a "$ROOTFS_MNT"/* / && rm -rf "$ROOTFS_MNT"

apk update
setup-hostname -n $HOST
apk add openrc openssh alpine-base curl syslinux util-linux sgdisk sudo bash
rc-update -q add devfs sysinit
rc-update -q add dmesg sysinit
rc-update -q add mdev sysinit
rc-update -q add hwdrivers sysinit

rc-update -q add hwclock boot
rc-update -q add modules boot
rc-update -q add sysctl boot
rc-update -q add hostname boot
rc-update -q add bootmisc boot
rc-update -q add syslog boot
rc-update -q add networking boot

rc-update -q add mount-ro shutdown
rc-update -q add killprocs shutdown
rc-update -q add savecache shutdown

rc-update -q add acpid default
rc-update -q add crond default
rc-update -q add sshd default
rc-update -q add cgroups default

ssh-keygen -A
echo features=\""$FEATURES"\" > /etc/mkinitfs/mkinitfs.conf

cat << EOF | tee /etc/update-extlinux.conf
overwrite=1
vesa_menu=0
default_kernel_opts="quiet"
modules=$MODULES
root=$ROOT
verbose=0
hidden=1
timeout=1
default=grsec
serial_port=
serial_baud=115200
xen_opts=dom0_mem=256M
password=''
EOF

cat <<EOF | tee /etc/fstab
$ROOT / ext4 rw,discard,errors=remount-ro 0 1
EOF

adduser --disabled-password --gecos "" $XUSER sudo && echo "$XUSER:$HOST" | chpasswd
mkdir -p /home/$XUSER/.ssh && echo "$PEM" >> /home/$XUSER/.ssh/authorized_keys
{
    echo "$PEM"
} >> /home/$XUSER/.ssh/authorized_keys
chmod 600 /home/$XUSER/.ssh/authorized_keys && chown -R "$XUSER:root" /home/$XUSER/.ssh
echo "$XUSER ALL=(ALL) NOPASSWD:ALL" | tee -a /etc/sudoers.d/$XUSER

apk add linux-lts
extlinux --install /boot
sgdisk "$ROOT_DEV" --attributes=1:set:2
dd bs=440 count=1 conv=notrunc if="$BOOT_LIB"/gptmbr.bin of="$ROOT_DEV"
sync; reboot -f

Just another update that it could works after reboot, but with linux-virt instead of linux-lts on the VM machine. But to be mentioned that the Alpine 3.19 seems cannot detect the /boot/extlinux.conf, so I moved it to /boot/syslinux.cfg.

#!/bin/dash

_OS=alpine
_ARC=$(dpkg --print-architecture)
_MIRROR=http://images.linuxcontainers.org
_FILTERED_INDEX=$(curl -fsSL "${_MIRROR}/meta/1.0/index-system" | grep -v edge)
_INDEX=$(echo "$_FILTERED_INDEX" | awk -F';' -v os="$_OS" -v arch="$_ARC" '$1==os && $3==arch {print $NF}' | tail -1)
_TARGET="${_MIRROR}/${_INDEX}rootfs.tar.xz"

XUSER=m0nius
HOST=computing-alpine
ROOTFS_MNT=/mnt.$_ARC
PEM="ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBUG8QsUdArpYbyQPgXIYISf6G2q9t6s+qxP5K8Vafc6"
FEATURES="ata base ide scsi usb virtio ext4 network"
MODULES="sd-mod,usb-storage,ext4,e1000e"

ROOT=$(findmnt -no SOURCE /)
ROOT_DEV="/dev/$(lsblk -ndo pkname "$ROOT")"
BOOT_LIB="/usr/share/syslinux"

sudo mkdir -p "$ROOTFS_MNT"/boot
curl -fsSL "$_TARGET" | sudo tar -C "$ROOTFS_MNT" -xJ

IFACE=$(ip route get 8.8.8.8 | sed -n 's/.*dev \([^\ ]*\).*/\1/p' | head -n 1)
_IPV4=$(ip addr show dev "$IFACE" | awk '/inet /{print $2}' | cut -d' ' -f2)
_IPv6=$(ip addr show dev "$IFACE" | awk '/inet6 /{print $2}' | cut -d' ' -f2)
GATEWAY=$(ip route show default | awk '/default/ {print $3}')

cat <<EOF | sudo tee "$ROOTFS_MNT"/etc/network/interfaces
auto lo
iface lo inet loopback

auto $IFACE
iface $IFACE inet static
    address $_IPV4
    gateway $GATEWAY
EOF

cat <<EOF | sudo tee "$ROOTFS_MNT"/etc/resolv.conf
nameserver 1.1.1.1
EOF

find / \( ! -path '/dev/*' -and ! -path '/proc/*' -and ! -path '/sys/*' -and ! -path '/selinux/*' -and ! -path "$ROOTFS_MNT/*" \) -delete 2>/dev/null || true

"$ROOTFS_MNT/lib/ld-musl-x86_64.so.1" "$ROOTFS_MNT/bin/busybox" cp -a "$ROOTFS_MNT"/* / && rm -rf "$ROOTFS_MNT"

apk update
setup-hostname -n $HOST
apk add openrc openssh alpine-base curl syslinux util-linux sgdisk sudo bash
rc-update -q add devfs sysinit
rc-update -q add dmesg sysinit
rc-update -q add mdev sysinit
rc-update -q add hwdrivers sysinit

rc-update -q add hwclock boot
rc-update -q add modules boot
rc-update -q add sysctl boot
rc-update -q add hostname boot
rc-update -q add bootmisc boot
rc-update -q add syslog boot
rc-update -q add networking boot

rc-update -q add mount-ro shutdown
rc-update -q add killprocs shutdown
rc-update -q add savecache shutdown

rc-update -q add acpid default
rc-update -q add crond default
rc-update -q add sshd default
rc-update -q add cgroups default
ssh-keygen -A
echo features=\""$FEATURES"\" > /etc/mkinitfs/mkinitfs.conf

cat << EOF | tee /etc/update-extlinux.conf
overwrite=1
vesa_menu=0
default_kernel_opts="quiet"
modules=$MODULES
root=$ROOT
verbose=0
hidden=1
timeout=1
default=grsec
serial_port=
serial_baud=115200
xen_opts=dom0_mem=256M
password=''
EOF

apk add linux-virt

cat <<EOF | tee /etc/fstab
$ROOT / ext4 rw,discard,errors=remount-ro 0 1
EOF

mv /boot/extlinux.conf /boot/syslinux.cfg

adduser --disabled-password --gecos "" $XUSER sudo && echo "$XUSER:$HOST" | chpasswd
mkdir -p /home/$XUSER/.ssh && echo "$PEM" >> /home/$XUSER/.ssh/authorized_keys
{
    echo "$PEM"
} >> /home/$XUSER/.ssh/authorized_keys
chmod 600 /home/$XUSER/.ssh/authorized_keys && chown -R "$XUSER:root" /home/$XUSER/.ssh
echo "$XUSER ALL=(ALL) NOPASSWD:ALL" | tee -a /etc/sudoers.d/$XUSER

extlinux --install /boot
sgdisk "$ROOT_DEV" --attributes=1:set:2
dd bs=440 count=1 conv=notrunc if="$BOOT_LIB"/gptmbr.bin of="$ROOT_DEV"

sync; reboot -f

However, the weird thing that, when perform apk related things, it reports the error, ERROR: Unable to lock database: Read-only file system, ERROR: Failed to open apk database: Read-only file system

@Mon-ius
Copy link

Mon-ius commented Feb 23, 2024

Oh, man, Your MODULES="sd-mod,usb-storage,$ROOT_FS,e1000e" here make me confused for days.
I removed e1000e, then everything works perfect. It may be removed from the mainline of Alpine Linux 3.19, thus /etc/update-extlinux.conf cannot figure it out, which leads to the disk error, so I cannot perform operation due to DISK is read-only now 🤗

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment