Webmin + Portainer: Basic tools of the trade

when setting up a basic server like ubuntu on an older machine, these two pieces of software are almost life-saving in their simplicity and power. anytime i set up a headless type-1 hypervisor like ubuntu server, debian server, or fedora server, these are literally the first two things i install.
Webmin
webmin is basically a dashboard for your server. but it does more than just reporting on what is happening – it allows for full system control and modification without the use of the cli (command line interface, or bash).

as you can see by the menu on the left, there are quite a few categories of tools and options to choose from. in fact, there is so much here, i don’t even recommend you use the menu structure – i navigate by typing what i am looking for in the search box right above the menus.
a few things i regularly use webmin for:
- setting static IP addresses
- adjusting LVM size (aka expanding hard drive size)
- configuring raid volumes
- auto update of packages
- reading logs
- changing passwords
- scheduling cron jobs
- mounting nfs/smb shares
- setting system time/time zone
- access the command line
these are just some basics. anything you would usually do through the command line can be done through the webmin gui. if youre like me and hate command line, invest some time in this and learn how it works. you can single-handedly do everything you’ll ever need for your server through this.
to install webmin, copy and paste the commands below into the command line (as sudo), then never use the command line again if you don’t want to:
curl -o setup-repos.sh https://raw.githubusercontent.com/webmin/webmin/master/setup-repos.sh
sh setup-repos.sh
apt-get install webmin
you can now access your dashboard through a web browser at whatever you servers ip is (for this example i will use 192.168.1.100) and port 10000.
http://192.168.1.100:10000
Portainer
servers are made to do two things for most home users: be a place to store and share data as well as run apps. storing and sharing are accomplished with the zfs file systems (keeps data safely for long periods of time) and nfs/smb shares (allows other computers to connect with the server as if it were just an external usb hard drive you plug in).
as for apps, docker is the greatest thing since slice bread. you can run most things directly on the server, but without a head, you’ll be in the command line quite a bit. docker allows the use of containers to keep apps neat and tidy and allows for a greater level of control over them. docker hub has over 100,000 apps on it that are all free. to manage those apps, we use portainer.

portainer runs on docker, all while managing docker containers. its very meta. if you don’t have docker installed, copy and paste the following commands into the cli (as sudo):
apt-get install \
ca-certificates \
curl \
gnupg
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
chmod a+r /etc/apt/keyrings/docker.gpg
echo \
"deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
"$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
apt-get update
apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
to install portainer, copy the following commands to the cli (as sudo):
docker volume create portainer_data
docker run -d -p 8000:8000 -p 9443:9443 --name portainer --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer-ce:latest
now portainer can be accessed securely at port 9443. like the above example, if our address were 192.168.1.100, we would navigate to https://192.168.1.100:9443.
creating apps is simple with stacks. a stack is just a place to copy + paste a docker compose file, which is the best way to run portainer. for example, if i want to install an app like wordpress to host my own website, i would navigate to hub.docker.com and search for “wordpress”. when i see the top result with over 1 billion pulls, i know i am in the right spot. scrolling halfway down the page for the docker official image of wordpress, i find the docker compose example:

now i copy all of the text into a new stack, modifying what i need to for my system. in this example of wordpress, i would want to replace the placeholder values for the WORDPRESS_DB_HOST, _USER, _PASSWORD, and _DB_NAME with my own ideas and not the stock ones which could be easily hacked. after i’m done, i deploy the stack and navigate the the exposed port, which here is :8080 (http://192.168.1.100:8080) and i am greeted with the wordpress setup page. its thats simple.
the other great place to find docker compose files is github. most times i will simply google {some app name} docker compose and the first hit will take me straight there. copy + paste into portainer > deploy stack > start using.
Install this first
the one thing you want to do is keep your containers up to date. automating this process is super simple with a container called watchtower, which will auto-update an containers you have installed. here is my personal docker compose for watchtower with environment variables to update at 3 am (EST) every night and remove unused images after it does so:
version: "3"
services:
watchtower:
image: containrrr/watchtower
environment:
- TZ=America/New_York
- WATCHTOWER_CLEANUP=true
- WATCHTOWER_INCLUDE_STOPPED=true
- WATCHTOWER_SCHEDULE=0 0 3 * * *
restart: unless-stopped
volumes:
- /var/run/docker.sock:/var/run/docker.sock
Exceptions
there are some things you aren’t going to want to run in docker containers. the most obvious thing is a cloudflare tunnel. that needs to be on the bare metal since docker runs all its apps in little networked silos. by running the tunnel outside of docker, you can reference the exposed ports in the docker compose file simply with localhost as i do here:

similarly, i run my vpn outside of docker because, again, i don’t want it siloed within a container. i want the whole machine to run on the vpn, so i configure a cron job through webmin to run a “wg-quick up” on boot to initiate my wireguard tunnel, effectively forcing the whole machine through my vpn in a single command line.