Surface Go is almost a perfect Ubuntu machine. The only reason for “almost” being the lack of camera support. All else works out of box or with minor updates. While you can use the standard installation setup, I like to do it in a bit more involved setup.
Mind you, you will need to have a network adapter plugged during install as debootstrap
requires it and enabling wireless is one of things not working out of box. If that’s the problem, stick with the default install instead.
First you of course you need to boot from install USB. After booting into Ubuntu desktop installation one needs a root prompt. All further commands are going to need root credentials anyhow.
sudo -i
Now we can set a few variables - disk, pool, host name, and user name. This way we can use them going forward and avoid accidental mistakes. Just make sure to replace these values with ones appropriate for your system.
DISK=/dev/disk/by-id/^^ata_disk^^
HOST=^^desktop^^
USER=^^user^^
Disk setup is really minimal. If there was a chance of dual-boot, EFI partition would be too small. For multiple kernels, one would need to increase boot partition. However, considering Surface Go has 64 MB or disk space, keeping those partitions small is probably a better choice. And no, you cannot make EFI partition smaller than 32 GB despite not needing more than a few megs.
blkdiscard $DISK
sgdisk --zap-all $DISK
sgdisk -n1:1M:+63M -t1:EF00 -c1:EFI $DISK
sgdisk -n2:0:+448M -t2:8300 -c2:Boot $DISK
sgdisk -n3:0:0 -t3:8309 -c3:Ubuntu $DISK
sgdisk --print $DISK
Having boot and EFI partition unencrypted does offer advantages and having standard kernels exposed is not much of a security issue. However, one must encrypt root partition.
cryptsetup luksFormat -q --cipher aes-xts-plain64 --key-size 256 \
--pbkdf pbkdf2 --hash sha256 $DISK-part3
Since crypt device name is displayed on every startup, for Surface Go I like to use host name here.
cryptsetup luksOpen $DISK-part3 ${HOST^}
Now we can prepare all needed partitions.
yes | mkfs.ext4 /dev/mapper/${HOST^}
mkdir /mnt/install
mount /dev/mapper/${HOST^} /mnt/install/
yes | mkfs.ext4 $DISK-part2
mkdir /mnt/install/boot
mount $DISK-part2 /mnt/install/boot/
mkfs.msdos -F 32 -n EFI $DISK-part1
mkdir /mnt/install/boot/efi
mount $DISK-part1 /mnt/install/boot/efi
To start the fun we need debootstrap
package.
apt update ; apt install --yes debootstrap
And then we can get basic OS on the disk. This will take a while.
debootstrap focal /mnt/install/
Our newly copied system is lacking a few files and we should make sure they exist before proceeding.
echo $HOST > /mnt/install/etc/hostname
sed "s/ubuntu/$HOST/" /etc/hosts > /mnt/install/etc/hosts
sed '/cdrom/d' /etc/apt/sources.list > /mnt/install/etc/apt/sources.list
cp /etc/netplan/*.yaml /mnt/install/etc/netplan/
If you are installing via WiFi, you might as well copy your wireless credentials:
mkdir -p /mnt/install/etc/NetworkManager/system-connections/
cp /etc/NetworkManager/system-connections/* /mnt/install/etc/NetworkManager/system-connections/
Finally we’re ready to “chroot” into our new system.
mount --rbind /dev /mnt/install/dev
mount --rbind /proc /mnt/install/proc
mount --rbind /sys /mnt/install/sys
chroot /mnt/install \
/usr/bin/env DISK=$DISK HOST=$HOST USER=$USER \
bash --login
Let’s not forget to setup locale and time zone.
locale-gen --purge "en_US.UTF-8"
update-locale LANG=en_US.UTF-8 LANGUAGE=en_US
dpkg-reconfigure --frontend noninteractive locales
dpkg-reconfigure tzdata
Now we’re ready to onboard the latest Linux image and the boot environment packages.
apt install --yes --no-install-recommends linux-image-generic linux-headers-generic \
--yes initramfs-tools cryptsetup keyutils grub-efi-amd64-signed shim-signed tasksel
Since we’re dealing with encrypted data, we should auto mount it via crypttab
. If there are multiple encrypted drives or partitions, keyscript
really comes in handy to open them all with the same password. As it doesn’t have negative consequences, I just add it even for a single disk setup.
echo "${HOST^} UUID=$(blkid -s UUID -o value $DISK-part3) none \
luks,discard,initramfs,keyscript=decrypt_keyctl" >> /etc/crypttab
cat /etc/crypttab
To mount boot and EFI partition, we need to do some fstab
setup too:
echo "UUID=$(blkid -s UUID -o value /dev/mapper/${HOST^}) \
/ ext4 noatime,nofail,x-systemd.device-timeout=5s 0 1" >> /etc/fstab
echo "PARTUUID=$(blkid -s PARTUUID -o value $DISK-part2) \
/boot ext4 noatime,nofail,x-systemd.device-timeout=5s 0 1" >> /etc/fstab
echo "PARTUUID=$(blkid -s PARTUUID -o value $DISK-part1) \
/boot/efi vfat noatime,nofail,x-systemd.device-timeout=5s 0 1" >> /etc/fstab
cat /etc/fstab
Now we update our boot environment.
KERNEL=`ls /usr/lib/modules/ | cut -d/ -f1 | sed 's/linux-image-//'`
update-initramfs -u -k $KERNEL
Grub update is what makes EFI tick.
sed -i "s/^GRUB_CMDLINE_LINUX_DEFAULT.*/GRUB_CMDLINE_LINUX_DEFAULT=\"quiet splash mem_sleep_default=deep\"/" /etc/default/grub
update-grub
grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=Ubuntu \
--recheck --no-floppy
Finally we install out GUI environment. I personally like ubuntu-desktop-minimal
but you can opt for ubuntu-desktop
. In any case, it’ll take a considerable amount of time.
tasksel install ubuntu-desktop-minimal
Short package upgrade will not hurt.
apt update ; apt dist-upgrade --yes
The only remaining task before restart is to create the user, assign a few extra groups to it, and make sure its home has correct owner.
sudo adduser --disabled-password --gecos '' $USER
usermod -a -G adm,cdrom,dip,lpadmin,plugdev,sudo $USER
echo "$USER ALL=NOPASSWD:ALL" | sudo tee /etc/sudoers.d/$USER >/dev/null
passwd $USER
Before finishing it up, I like to install Surface Go WiFi and backlight tracer packages. This will allow for usage of wireless once we boot into installed system and for remembering light level between plugged/unplugged states.
wget -O /tmp/surface-go-wifi_amd64.deb \
https://www.medo64.com/download/surface-go-wifi_0.0.5_amd64.deb
apt install --yes /tmp/surface-go-wifi_amd64.deb
wget -O /tmp/backlight-tracer_amd64.deb \
https://www.medo64.com/download/backlight-tracer_0.1.1_all.deb
apt install --yes /tmp/backlight-tracer_amd64.deb
As install is ready, we can exit our chroot environment.
exit
And cleanup our mount points.
umount /mnt/install/boot/efi
umount /mnt/install/boot
mount | grep '/mnt/install/' | tac | awk '/\/mnt/ {print $3}' | xargs -i{} umount -lf {}
umount /mnt/install
After the reboot you should be able to enjoy your installation. Seeing errors is fine - just reboot manually if stuck.
reboot
Once booted I like to setup suspend to react on power button and and to disable automatic brightness changes.
gsettings set org.gnome.settings-daemon.plugins.power button-power 'suspend'
gsettings set org.gnome.settings-daemon.plugins.power power-button-action 'suspend'
gsettings set org.gnome.settings-daemon.plugins.power ambient-enabled 'false'
gsettings set org.gnome.mutter experimental-features "['x11-randr-fractional-scaling']"
My preferred scale factor is 150% (instead of default 200%) but you’ll need to change that in settings manually.