summary: "We recently murdered a server's terminal via `do_distro_upgrade`, and thought it'd be a good time to learn more about containers and Alpine."
---
# Rootless Containers on Alpine Linux
## Background
**(Ashe)**
So. We recently murdered a server's terminal via `do_distro_upgrade`.
**(Tammy)** Was it really that bad?
**(Ashe)** Yes.
```
% man 7z
WARNING: terminal is not fully functional
- (press RETURN)%
```
It was in fact *that bad*. So we figured, well, we can spend a few hours, days, whatever fixing this...
**(Tammy)** Or we could just build a new server!
**(Ashe)** Right.
So, after asking some friends about their opinions, we settled on Alpine Linux. And why not also migrate all of our pm2 workloads to containers while we're at it? We've been meaning to learn more about containers for a while now.
So off we go!
## Prep Work
We need a few things before we actually set up rootless containers. We'll be following along with the [Official Rootless Containers Tutorial](https://rootlesscontaine.rs/getting-started/common/), making adjustments as necessary.
### Login Information
Most Rootless Container implementations use `$XDG_RUNTIME_DIR` to find the user's ID and where their runtime lives (usually some subdir of `/run/user/`).
Systemd-based Linux distros will handle this automatically, but Alpine uses [OpenRC](https://wiki.alpinelinux.org/wiki/OpenRC), which does not do this automatically.
While Alpine doesn't provide a tutorial for Rootless Containers, we can adapt some of the prep work done for [Wayland](https://wiki.alpinelinux.org/wiki/Wayland) to get OpenRC to set `$XDG_RUNTIME_DIR` for us.
We just create `/etc/profile.d/xdg_runtime_dir.sh` like so:
Rootless Containers generally expect `/etc/subuid` and `/etc/subgid` to contain at least 65,536 sub-IDs for each user.
`shadow-subids` doed create these files for us, but leaves them empty by default, so let's go ahead and do that.
The [page on subIDs](https://rootlesscontaine.rs/getting-started/common/subuid/) provides a handy Python script to do that for us, which we'll edit slightly so it's not writing directly to system files:
```python
f = open("subuid", "w")
for uid in range(1000, 65536):
f.write("%d:%d:65536\n" %(uid,uid*65536))
f.close()
f = open("subgid", "w")
for uid in range(1000, 65536):
f.write("%d:%d:65536\n" %(uid,uid*65536))
f.close()
```
This is probably overkill for our use-case, but that's also fine.
**(Doll)** So this one just runs script and copies to /etc/?
**(Ashe)** Yes Doll, that's right.
With that done, we can move onto the last prep step.
### CGroups V2
To limit resources that a container can use, we need to enable CGroups V2. In OpenRC, this can be done by changing some options in `/etc/rc.conf`.
To enable CGroups in general, we need to set `rc_controller_cgroups` to `YES`
```sh
# This switch controls whether or not cgroups version 1 controllers are
We'll be using nerdctl as our containerd controller of choice. It comes with a rootless containerd.service, but since Alpine doesn't use systemd, we'll have to adapt this into an rc service.
We can adapt the [install script](https://github.com/containerd/nerdctl/blob/48f189a53a24c12838433f5bb5dd57f536816a8a/extras/rootless/containerd-rootless-setuptool.sh) nerdctl provides to our purposes.