
This marks the very re-beginning of my homelab journey. I don’t have much to start with - just an old, dusty laptop repurposed as a server and a VPS on AWS cloud. (Yeah, I know using the cloud doesn’t technically make it a “homelab”, but that’s what I’m going with.)
PS: This is my second attempt at building a homelab. There’s nothing wrong with the existing one, but it’s the new year time, and I wanted to start fresh and try something different this time.
Provisioning an EC2 instance on AWS using Terraform
We are going to spin up a t3.medium EC2 instance using the latest NixOS AMI.
terraform {
cloud {
organization = "murtaza-u"
workspaces {
name = "homelab"
}
}
required_providers {
aws = {
source = "hashicorp/aws"
version = "5.81.0"
}
}
}
provider "aws" {
region = "ap-south-1"
default_tags {
tags = {
Name = var.srv_cloud_0.instance_name
}
}
}
# fetching AWS AMI image
data "aws_ami" "nixos" {
owners = ["427812963091"]
most_recent = true
filter {
name = "name"
values = ["nixos/24.11*"]
}
filter {
name = "architecture"
values = ["x86_64"] # or "arm64"
}
}
# creating SSH key pair
resource "aws_key_pair" "key_pair" {
key_name = var.srv_cloud_0.instance_name
public_key = var.srv_cloud_0.public_key
}
### security group ###
resource "aws_vpc_security_group_egress_rule" "allow_all" {
description = "Allow all outbound traffic"
security_group_id = aws_security_group.srv_cloud_0.id
ip_protocol = "-1"
cidr_ipv4 = "0.0.0.0/0"
}
resource "aws_vpc_security_group_ingress_rule" "allow_ssh" {
description = "Allow SSH inbound traffic"
security_group_id = aws_security_group.srv_cloud_0.id
ip_protocol = "tcp"
from_port = 22
to_port = 22
cidr_ipv4 = "0.0.0.0/0"
}
resource "aws_vpc_security_group_ingress_rule" "allow_http" {
description = "Allow http inbound traffic"
security_group_id = aws_security_group.srv_cloud_0.id
ip_protocol = "tcp"
from_port = 80
to_port = 80
cidr_ipv4 = "0.0.0.0/0"
}
resource "aws_vpc_security_group_ingress_rule" "allow_https" {
description = "Allow https inbound traffic"
security_group_id = aws_security_group.srv_cloud_0.id
ip_protocol = "tcp"
from_port = 443
to_port = 443
cidr_ipv4 = "0.0.0.0/0"
}
resource "aws_security_group" "srv_cloud_0" {
name = var.srv_cloud_0.instance_name
description = "Firewall rules for ${var.srv_cloud_0.instance_name}"
}
### END - security group ###
# creating and associating an elastic IP
resource "aws_eip" "srv_cloud_0" {
instance = aws_instance.srv_cloud_0.id
domain = "vpc"
}
# launching an EC2 instance
resource "aws_instance" "srv_cloud_0" {
ami = data.aws_ami.nixos.id
instance_type = var.srv_cloud_0.instance_type
root_block_device {
volume_size = var.srv_cloud_0.root_block_device.v_size
volume_type = var.srv_cloud_0.root_block_device.v_type
delete_on_termination = true
}
associate_public_ip_address = true
key_name = aws_key_pair.key_pair.key_name
vpc_security_group_ids = [aws_security_group.srv_cloud_0.id]
}
variable "aws_region" {
type = string
default = "ap-south-1"
description = "AWS region to deploy resources to"
}
variable "srv_cloud_0" {
type = object({
instance_type = string
instance_name = string
root_block_device = object({
v_size = number
v_type = string
})
public_key = string
})
description = "Configurations related to the `srv-cloud-0` EC2 instance"
}
output "srv_cloud_0_eip" {
value = aws_eip.srv_cloud_0.public_ip
description = "AWS elastic ip address associated with `srv-cloud-0`"
}
Now that that’s out of the way, let’s write the NixOS config for our instance.
NixOS
I organize my configuration into reusable modules. Here is the directory structure commonly used in the Nix community:
.
├── flake.lock
├── flake.nix
├── hosts
│ └── srv-cloud-0
│ └── default.nix
└── modules
├── default.nix
└── platform
├── default.nix
├── nix.nix
├── ssh.nix
├── synctime.nix
└── users.nix
I have extracted out pieces of code that will be reused for configuring my second host into separate modules.
Here is how the hosts/srv-cloud-0/default.nix file looks like,
{ modulesPath, ... }:
{
imports = [
"${modulesPath}/virtualisation/amazon-image.nix"
];
# Set hostname.
networking.hostName = "srv-cloud-0";
# Set your time zone.
time.timeZone = "Etc/UTC";
platform = {
# enable flakes & configure gc
nix.enable = true;
# setup default users
users.enable = true;
# enable openssh
ssh.enable = true;
# enable timesyncd service
synctime.enable = true;
};
# Open ports in the firewall.
networking.firewall = {
enable = true;
allowedTCPPorts = [
22 # ssh
80 # traefik http
443 # traefik https
];
allowedUDPPorts = [ ];
};
}
You can find the source code for my homelab setup on my github.
Re-purposing an old laptop as my second host
I have an old Sony laptop lying around, collecting dust. It is probably a decade old by now. It has a quad-core CPU, 4GB of memory, and 256GB of storage. I’ve configured my router’s DHCP settings to always assign it the IP address 192.168.29.05.

I’ve already installed and configured NixOS on it. The configuration is very similar to our srv-cloud-0 host. For brevity, I’ll only mention the changed parts.
# Setting GRUB boot loader.
boot.loader.grub.enable = true;
# Define on which hard drive you want to install Grub.
boot.loader.grub.device = "/dev/sda"; # or "nodev" for efi only
# Disable suspend.
# We are using a laptop as a server here, so we want it to keep running even
# when the lid is closed.
systemd.targets.sleep.enable = false;
services.logind.lidSwitch = "ignore";
# Set hostname.
networking.hostName = "srv-onprem-0";
├── hosts
│ ├── srv-cloud-0
│ │ └── default.nix
│ └── srv-onprem-0
│ ├── default.nix
│ └── hardware.nix
Since this is a bare-metal host, the configuration also include the hardware configuration (hosts/srv-onprem-0/hardware.nix). This is automatically generated by nixos-generate-config during the installation.
Lastly, here is the nixosConfiguration block in flake.nix that brings everything together,
nixosConfigurations = {
srv-cloud-0 = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
{
nix.registry.nixpkgs.flake = nixpkgs;
system.stateVersion = "24.11";
}
./modules
./hosts/srv-cloud-0
];
};
srv-onprem-0 = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
{
nix.registry.nixpkgs.flake = nixpkgs;
system.stateVersion = "24.11";
}
./modules
./hosts/srv-onprem-0
];
};
};
To update my hosts with the new configuration changes, I can run:
nixos-rebuild --flake .#srv-onprem-0 --target-host srv-onprem-0 --use-remote-sudo switch
nixos-rebuild --flake .#srv-cloud-0 --target-host srv-cloud-0 --use-remote-sudo switch
Where, srv-onprem-0 & srv-cloud-0 point to hosts in my ssh config,
Host srv-cloud-0
User murtaza
Hostname <ip>
IdentityFile ~/.ssh/homelab
Host srv-onprem-0
User murtaza
Hostname 192.168.29.05
IdentityFile ~/.ssh/homelab