Debian 12 LVM
Overview
This guide covers the deployment and configuration of Debian 12 with an LVM root layout on Microsoft Azure, using SecureAnyCloud's preconfigured virtual machine image from the Azure Marketplace.
Debian 12, codenamed bookworm, is the current oldstable release of Debian, one of the oldest and most widely deployed Linux distributions. It is valued for slow, predictable change: the kernel, toolchain and library surface stay fixed for the life of the release.
What makes this image different from the standard Debian 12 image is the disk layout. The root filesystem lives on an LVM logical volume (debian-vg/root, the same naming that the Debian installer's own guided LVM option produces) on top of a physical volume partition, instead of on a fixed raw partition.
You can therefore grow the root filesystem online, on the running system, with no reboot and no unmount. You can also reshape storage later with standard LVM tooling.
What you get:
- Debian 12 (bookworm) with root on LVM: GPT disk with an EFI system partition, a 2 GiB
/bootpartition, and an LVM physical volume carrying volume groupdebian-vgwith logical volumerootas/(ext4) - The full LVM and grow toolchain:
lvm2,growpart,pvresize,lvextend,resize2fs. Everything needed to grow or reshape storage is present, and nothing is removed from the standard package set - Every available security update applied at build time
unattended-upgradesarmed and verified against the Debian security suite, so the machine keeps applying security updates on its own- Azure Linux Agent (walinuxagent) and cloud-init for Azure integration
- chrony for NTP time synchronization
- AppArmor in enforcing mode (the Debian default since Debian 10)
- LLMNR disabled, so the system resolver listens on loopback only
- Gen2 Hyper-V virtual machine support
- 24/7 SecureAnyCloud support
- Platform: Microsoft Azure (Gen2 Hyper-V, x64)
- Default user:
azureuser
Security posture
This image ships with no known credential. There is no default password and no baked-in SSH key:
- The root account password is locked, so no one can log in as root with a password.
PasswordAuthenticationis disabled in the SSH daemon, so every login is by key.PermitRootLoginis set toprohibit-password, so root cannot be reached with a password even if one were later set.- The only key that works is the public key you supply when you create the virtual machine. Azure injects it into
azureuserat first boot, so no two machines you launch ever share a secret. - The machine identity and the SSH host keys are regenerated uniquely on every instance, so no two machines share a host key either.
- AppArmor is enabled and enforcing, the Debian default.
- LLMNR is disabled, so the only service reachable from off the machine is SSH. LLMNR is a legacy link-local name resolution protocol with a well-known spoofing attack class, and it has no use on an Azure virtual network.
Prerequisites
Before you deploy this image, make sure that you have:
- An active Microsoft Azure subscription
- Access to the Azure Portal or Azure CLI
- An SSH key pair for Linux VM access
- Familiarity with Azure VM management
Recommended VM size: Standard_B2s (2 vCPU, 4 GB RAM) or larger.
Disk size: the image ships as a 30 GiB OS disk, with the root volume taking all of its LVM space.
If you want a larger root, set a bigger OS disk size at create time (for example 64 GiB). Then grow the root volume online as shown in Step 7. That is exactly what this image is for.
Deploy the Virtual Machine
Option A: Azure Portal
- Open the Azure Marketplace and search for "Debian 12 LVM SecureAnyCloud"
- Select the image and click Create
- Configure the basics:
- Subscription: select your Azure subscription
- Resource group: create a new one or select an existing one
- Virtual machine name: enter a name for your VM
- Region: select your preferred Azure region
- Size:
Standard_B2sis recommended - Under Administrator account, select SSH public key and enter your key
- Under Inbound port rules, allow SSH (port 22)
- On the Disks tab, set the OS disk size you want (larger than 30 GiB if you plan to grow the root)
- Click Review + create, then Create
Option B: Azure CLI
az vm image terms accept --urn sac1647283583153:debian-12-lvm:default:latest
az vm create \
--resource-group myResourceGroup \
--name my-debian-12-lvm-vm \
--image sac1647283583153:debian-12-lvm:default:latest \
--size Standard_B2s \
--os-disk-size-gb 64 \
--admin-username azureuser \
--generate-ssh-keys \
--public-ip-sku Standard--os-disk-size-gb 64 deploys the 30 GiB image onto a 64 GiB disk. You claim the extra space online in Step 7 with three LVM commands.
The image ships no password and no authorized key of its own. The only credential that will ever work is the SSH public key that you supply here.
Connect via SSH
Run these on your own workstation, not on the VM. Find the public IP, then connect:
az vm show --resource-group myResourceGroup --name my-debian-12-lvm-vm --show-details --query publicIps -o tsv ssh azureuser@<vm-ip>There is no password to enter. If SSH asks you for one, your client is not offering the key that you supplied at create time.
Confirm the release and the LVM layout
Check that the machine is the Debian 12 release that you expect:
grep -E '^(NAME|VERSION|ID|VERSION_ID|PRETTY_NAME)=' /etc/os-release cat /etc/debian_version uname -srm dpkg --print-architectureExpected output:
PRETTY_NAME="Debian GNU/Linux 12 (bookworm)" NAME="Debian GNU/Linux" VERSION_ID="12" VERSION="12 (bookworm)" ID=debian 12.15 Linux 6.1.0-53-cloud-amd64 x86_64 amd64Then confirm the defining property of this image: the root filesystem on an LVM logical volume.
findmnt -no SOURCE,FSTYPE /
sudo pvs
sudo vgs
sudo lvsExpected output on a fresh VM deployed with the default 30 GiB disk:
/dev/mapper/debian--vg-root ext4 PV VG Fmt Attr PSize PFree /dev/sda3 debian-vg lvm2 a-- <27.50g 0 VG #PV #LV #SN Attr VSize VFree debian-vg 1 1 0 wz--n- <27.50g 0 LV VG Attr LSize Pool Origin Data% Meta% Move Log Cpy%Sync Convert root debian-vg -wi-ao---- <27.50g/ on /dev/mapper/debian--vg-root is what the standard image cannot give you: a root volume that you can extend online and a volume group that you can reshape.
The disk itself is GPT with a 512 MiB EFI system partition, a 2 GiB /boot partition, and the rest as the LVM physical volume. The boot partition is a plain partition so that the bootloader never depends on LVM activation.
VFree 0 is deliberate. The image ships with the root volume taking every extent, and growth comes from enlarging the disk, not from space held back.
Check the patch level
The image is fully updated at build time. Confirm that no upgrades are pending and that the update path is intact:
apt-get -s -o APT::Get::Always-Include-Phased-Updates=true dist-upgrade | grep -c '^Inst ' | sed 's/^/pending upgrades: /' apt-mark showhold dpkg -l | awk '/linux-image-[0-9]/{print $2, $3}'Expected output shows pending upgrades: 0, no held packages, and exactly one installed Debian cloud kernel:
pending upgrades: 0 linux-image-6.1.0-53-cloud-amd64 6.1.187-1Exactly one kernel package ships on purpose. A superseded kernel left installed would keep matching its own security advisories even though it never boots.
This image also arms unattended-upgrades against the Debian security suite, so the machine downloads and applies security updates on a timer without any action from you. You can still run updates on your own schedule:
sudo apt-get update && sudo apt-get -y dist-upgradeReboot if a new kernel was installed:
sudo systemctl rebootVerify the security posture
Confirm that AppArmor is enforcing, root is locked, and SSH is key-only:
sudo aa-status --enabled && echo "apparmor: enabled"
sudo passwd -S root
sudo sshd -T | grep -E '^(permitrootlogin|passwordauthentication|pubkeyauthentication) 'Expected output:
apparmor: enabled root L 2009-12-22 -1 -1 -1 -1 permitrootlogin without-password pubkeyauthentication yes passwordauthentication nowithout-password is how OpenSSH reports the prohibit-password setting. Both names mean the same thing: root may not authenticate with a password. The L from passwd -S means the root account is locked.
Verify Azure integration and unattended updates
Confirm that the Azure Linux Agent, cloud-init and time synchronization are healthy, that unattended security updates are armed, and check your resources:
systemctl is-active walinuxagent 2>/dev/null || systemctl is-active waagent cloud-init --version
systemctl is-enabled apt-daily-upgrade.timer apt-config dump | grep -E 'APT::Periodic::(Update-Package-Lists|Unattended-Upgrade) '
systemctl is-active chrony df -h /Expected output:
active /usr/bin/cloud-init 22.4.2 enabled APT::Periodic::Update-Package-Lists "1"; APT::Periodic::Unattended-Upgrade "1"; active Filesystem Size Used Avail Use% Mounted on /dev/mapper/debian--vg-root 27G 704M 25G 3% /The Azure Linux Agent lets Azure provision your SSH key, run extensions and report VM health, so it should always be active.
The enabled state of apt-daily-upgrade.timer, together with Unattended-Upgrade "1" in the apt periodic config, keeps the machine applying security updates on its own after launch.
Grow the root filesystem online
This is the reason to choose the LVM image. If you deployed with an OS disk larger than 30 GiB (as in the Step 1 CLI example), the extra space is sitting unclaimed beyond the LVM partition. You claim it on the running system, with no reboot, no unmount and no downtime.
Know this first, as measured on this exact image: cloud-init does not claim the space for you. Its growpart module skips LVM roots by design. /var/log/cloud-init.log records '/' SKIPPED: Resizing mapped device (/dev/mapper/debian--vg-root) skipped as it is not encrypted.
On first boot, the partition, the physical volume and the root volume therefore stay at their shipped 27.5 GiB. That is standard behavior for an LVM root, and it leaves you in control of when and how the space is used.
The chain below claims all of the space into /. Run it as is, or stop after pvresize if you would rather keep the space free in the volume group for other volumes.
DISK="$(readlink -f /dev/disk/azure/root)"
sudo growpart "${DISK}" 3
sudo pvresize "${DISK}3"
sudo lvextend -l +100%FREE /dev/debian-vg/root
sudo resize2fs /dev/debian-vg/root df -h /
FREE=$(sudo vgs --noheadings -o vg_free_count debian-vg | tr -dc '0-9')
PVB=$(sudo pvs --noheadings --units b --nosuffix -o pv_size "${DISK}3" | tr -dc '0-9')
PARTB=$(( $(cat /sys/class/block/$(basename "${DISK}")3/size) * 512 )) if [ "${FREE:-1}" -ne 0 ]; then echo "FAIL: ${FREE} free extents remain in VG debian-vg"; exit 1; fi if [ "$(( PARTB - PVB ))" -gt 8388608 ]; then echo "FAIL: pvresize did not take - partition ${PARTB}B vs physical volume ${PVB}B"; exit 1; fi echo "root volume fully extended: 0 free extents in VG debian-vg, and the physical volume fills the partition"Expected output on a 64 GiB disk, exactly as produced by this image:
CHANGED: partition=3 start=5244928 old: size=57667584 end=62912511 new: size=128972767 end=134217694 Physical volume "/dev/sdb3" changed 1 physical volume(s) resized or updated / 0 physical volume(s) not resized Size of logical volume debian-vg/root changed from <27.50 GiB (7039 extents) to <61.50 GiB (15743 extents). Logical volume debian-vg/root successfully resized. resize2fs 1.47.0 (5-Feb-2023) Filesystem at /dev/debian-vg/root is mounted on /; on-line resizing required old_desc_blocks = 4, new_desc_blocks = 8 The filesystem on /dev/debian-vg/root is now 16120832 (4k) blocks long. Filesystem Size Used Avail Use% Mounted on /dev/mapper/debian--vg-root 61G 700M 57G 2% / root volume fully extended: 0 free extents in VG debian-vg, and the physical volume fills the partitionresize2fs says on-line resizing required and resizes the mounted root, so the whole chain runs on the live system.
The device shown by readlink may differ between boots (/dev/sda on one boot, /dev/sdb on another). The /dev/disk/azure/root link always points at the OS disk, which is why the chain uses it.
The two checks at the end are worth keeping in your own automation. vg_free_count at 0 proves that lvextend claimed everything. The physical volume filling the partition (to within LVM's small metadata gap) proves that pvresize actually took. Each check catches a failure that the other cannot see.
On this image, growpart also runs an LVM pvresize of its own after growing the partition. The explicit pvresize in the chain is therefore an idempotent re-assertion. Keep it, because it also covers partitions grown by other tools.
If the disk was already fully claimed, growpart reports NOCHANGE and exits non-zero, lvextend reports that the size already matches, and the final checks still pass. The chain is safe to re-run.
Growing an existing VM later: enlarge the OS disk first. Deallocate the VM, run az disk update --resource-group <rg> --name <os-disk-name> --size-gb <new-size>, and start the VM, or use the portal's Disks blade. Then run the same chain.
The LVM layout also lets you go further than root growth. Attach a data disk and add it to the volume group with sudo pvcreate on the new disk's partition and sudo vgextend debian-vg. Then carve dedicated volumes for data or logs with sudo lvcreate, all without touching the root filesystem.
Networking and the firewall
The Azure Network Security Group is the control plane for inbound and outbound traffic. It is the first, and usually the only, place where you need to open a port.
The image advertises a single inbound port, SSH on 22, and that is also the only service listening off the machine:
sudo ss -tlnpOn a fresh machine, the listeners are sshd on port 22 (IPv4 and IPv6) and systemd-resolved on the loopback-only addresses 127.0.0.53:53 and 127.0.0.54:53.
LLMNR is disabled on this image, so the resolver does not open the off-box port 5355 that a stock Debian 12 would. Only port 22 is reachable from outside, governed by your NSG.
Debian ships no active host firewall by default, so your NSG rules take effect exactly as written, with no second layer to keep in sync.
If your policy requires a host firewall as well, Debian's own firewall framework is nftables (already installed), or you can install ufw for a simpler frontend. Add the SSH rule first, before enabling, so that you do not lock yourself out:
sudo apt-get install -y ufw
sudo ufw allow OpenSSH
sudo ufw enableOnce a host firewall is running, a port must be open in both the Azure NSG and the host firewall before traffic reaches your service.
Install software
The Debian archive carries the application packages. Search it, then install what you need:
apt-cache search nginx 2>/dev/null | sed -n '1,5p'Install a package and enable it in the usual way, for example sudo apt-get install -y nginx followed by sudo systemctl enable --now nginx.
To add another administrator, create the account with sudo adduser <name>. Add it to the sudo group with sudo usermod -aG sudo <name>.
Then copy your public key into /home/<name>/.ssh/authorized_keys with mode 600, and give the .ssh directory mode 700, owned by that user.
The image ships with no human accounts at all, so azureuser is the only login until you add one:
getent group sudoExpected output:
sudo:x:27:azureuserCommon use cases
- Workloads whose storage needs will grow: extend the root online instead of rebuilding the VM
- Servers that will gain dedicated volumes later, for data, logs or applications
- Teams whose build standard requires LVM-managed disks on every server
- A stable, fully supported Debian base for any workload
Troubleshooting
Cannot connect via SSH
- Verify that the VM is in the Running state in the Azure Portal
- Check that port 22 is allowed in the Network Security Group
- Make sure that you are using the correct username:
azureuser - Verify that your SSH key matches the one you supplied at create time. Password login is disabled by design, so a wrong key cannot fall back to a password prompt.
To see which key your client is actually offering, run ssh -v azureuser@<vm-ip> from your workstation and look for the Offering public key lines.
df shows 27G on a bigger disk
That is the expected state before you run Step 7. cloud-init deliberately does not grow an LVM root, so a VM deployed on a 64 GiB disk boots with the shipped 27.5 GiB root volume until you claim the space.
Run the Step 7 chain. It runs online and takes seconds.
growpart says NOCHANGE
The partition already fills the disk. growpart exits non-zero on NOCHANGE, which is informational, not an error.
If you expected new space, confirm that the OS disk resize actually happened (az disk show --query diskSizeGb) and that the VM was restarted after an offline disk resize.
Azure agent not running
sudo systemctl status walinuxagentIf it is not running, start it on the VM:
sudo systemctl enable --now walinuxagentPackage manager issues
Refresh the metadata cache first:
sudo apt-get updateIf repositories are unreachable, check DNS resolution. getent is always present and resolves through the same system resolver:
getent hosts deb.debian.orgError-priority journal lines at boot
A few error-priority journal lines appear on every boot of a Debian 12 image on Azure. They are expected and harmless:
- The kernel's
RETBleed: WARNING: Spectre v2 mitigation leaves CPU vulnerable to RETBleed attacksnotice. It is a CPU mitigation report, not a fault in the machine. /dev/sr0: Can't open blockdev, which refers to the emulated CD-ROM drive.- A pair of
dhclient ... execve (/bin/true, ...): Permission deniedlines with aTimeout too large reducing to: 2147483646companion. This is cloud-init's short-lived first-boot DHCP probe runningdhclientwith a no-op script that the stock AppArmor dhclient profile denies. systemd-networkd applies the real network configuration, and it is unaffected.
None of these prevents boot, SSH or networking. Beyond that, a healthy machine has no AppArmor denials in the kernel log at all:
sudo journalctl -k --no-pager | awk '/apparmor/ && /DENIED/{n++} END{print "apparmor denials: " n+0}'Expected output:
apparmor denials: 0Important notes
Debian is free and open-source software. It is not covered by a single license. It is a distribution assembled from thousands of independently packaged components, each carrying its own terms, including the GPL, LGPL, MIT, BSD and Apache 2.0.
Every package's terms are readable under /usr/share/doc/<package>/copyright. This image enables only Debian's DFSG-free main component. No subscription or license key is required to run this image.
Debian 12 is the current oldstable release. It continues to receive security updates through the Debian LTS project, published to the standard mirrors free of charge, until 30 June 2028. The image updates itself from the same bookworm-security suite throughout.
Plan a move to a newer stable release for workloads that need to outlive that window.
Debian is a trademark of Software in the Public Interest, Inc. The name is used here nominatively, only to identify the distribution this image contains. SecureAnyCloud is not affiliated with, endorsed by or sponsored by the Debian Project or Software in the Public Interest, Inc.
Support
For help with this image, contact SecureAnyCloud support:
- Website: secureanycloud.com
- Product catalog: secureanycloud.com/products
- User guides: secureanycloud.com/guides
- SLA: 24-hour response guaranteed
Deploy on Azure
Launch Debian 12 LVM on Azure with 24/7 support from SecureAnyCloud.
View this image
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com
Overview
This guide covers launching and verifying the Debian 12 LVM AMI from SecureAnyCloud on Amazon EC2. It is one version of the SecureAnyCloud Debian listing on AWS Marketplace. You pick it as the software version when you subscribe and launch. Every release and variant in the listing is described at /guides/debian-server-aws/.
Debian 12 "bookworm" is the Debian Project's free, community-built operating system. Its regular security support ended in June 2026, and the Debian LTS team now maintains it. Security updates continue until June 2028 through the same bookworm-security archive.
This version is built from Debian's own official AMI. What you get is five additions:
- An LVM disk layout. The root filesystem and the six locations the CIS benchmark wants on their own partitions (
/home,/tmp,/var,/var/tmp,/var/logand/var/log/audit) are separate logical volumes in the volume groupdebian-vg, each mounted with the benchmark's options (nodev,nosuid, andnoexecwhere it applies). You can grow any of them online. - CIS Level 1 Server remediation. The image is remediated with the OpenSCAP CIS Level 1 Server profile from SCAP Security Guide 0.1.82 (the
ssg-debian12content, CIS Debian Linux 12 Benchmark v1.1.0), then rebooted and evaluated. On this build, 352 of 359 scored rules pass (98.1%), and a re-run on a freshly launched instance gives the same result. - Real volumes, real scoring. Because the volumes are real, the partition and mount-option rules are applied and scored, not waived. Five rules are waived because they cannot work on an EC2 image, each with its reason. The HTML report, the machine-readable results, the waiver list and a summary ship in
/usr/share/doc/sac/. - Fixes for what the profile's own remediation leaves undone.
nullokis removed from PAM (the content's fix could not apply it). Martian-packet logging stays on after ufw starts. The AIDE check that the profile adds runs Debian'saidebinary. The AIDE log files belong to the_aideaccount so that Debian's own daily AIDE check can rotate them. The instance boots, and keeps running day after day, with no failed services. - EC2 defaults that keep you in control. Key-only SSH as
admin, root locked, no password on any account, AppArmor enforcing, IMDSv2 required, a host firewall (ufw) that allows SSH in and everything out, the AWS Systems Manager agent, and time from the Amazon Time Sync Service. - Fully patched at build time, with
unattended-upgradesinstalling Debian security updates on your instance. - What is on the image: Debian 12 (the Debian
cloud-amd64kernel), LVM2, cloud-init, OpenSSH, ufw, chrony, AppArmor, the AWS Systems Manager agent, rsyslog, AIDE file integrity checking, exim4 for local mail only (loopback, no inbound SMTP),unattended-upgrades, the AWS CLI version 2 from Debian's archive (awscli), OpenSCAP and the pinned SCAP Security Guide content used for the evaluation. - Not installed: the Linux audit daemon (
auditd). The CIS Level 1 Server profile does not require it. Runsudo apt-get -y install auditdto add it if you need it. - Default user:
admin - Architecture: x86_64
- Root volume: 12 GiB gp3, LVM (volume group
debian-vg, ext4 logical volumes, 820 MiB left unallocated for you to assign)
These images are remediated with an OpenSCAP profile and ship their own evaluation report. They are not CIS Hardened Images, and CIS has not reviewed or certified them.
Connecting to your instance
- Version: Debian 12 LVM
- SSH user: admin
- Root device: /dev/xvda (12 GiB gp3, LVM)
Security posture
- No known credential. There is no default password and no baked-in SSH key. The only key that works is the EC2 key pair you choose at launch, which cloud-init installs for
adminon first boot. - Key-only SSH.
PasswordAuthentication no,PermitRootLogin no, keyboard-interactive and X11 forwarding off. The root account has no password and is locked. - Per-instance identity. SSH host keys and the machine ID are generated fresh on every instance, so no two instances share a host key.
- IMDSv2 required. The AMI is registered with
ImdsSupport v2.0, so instances launched from it refuse token-less (IMDSv1) metadata requests by default. - Separate, restricted volumes.
/tmp,/var/tmp,/var/logand/var/log/auditare mountednoexec,nodev,nosuid./homeand/vararenodev,nosuid. A full log volume cannot fill the root filesystem. - AppArmor enforcing, with every loaded profile in enforce mode, as Debian ships it.
- Host firewall. ufw denies inbound traffic except SSH and allows all outbound traffic, so the SSM agent, metadata service, DNS, time and package updates keep working.
- Only SSH listens on the network. The DNS stub resolver and the local mail service listen on loopback only.
Prerequisites
- An AWS account and permission to launch EC2 instances
- An EC2 key pair in the Region you launch in
- A security group that allows TCP 22 from your own address (not from the whole internet)
Recommended instance type: m5.large. The image runs on any current x86_64 instance type.
Launch the instance
Option A: AWS Marketplace console
- Open the SecureAnyCloud Debian listing on AWS Marketplace. Choose View purchase options, then Subscribe. One subscription covers every version in the listing.
- Once the subscription is active, choose Launch your software.
- Choose One-click launch from AWS Marketplace (or Launch from EC2 Console for full control). In Version, choose Debian 12 LVM - YYYY-MM-DD (the date is when the version was added), and select your Region.
- Instance type:
m5.largeis recommended, and any current x86_64 type works. The software charge is $0.04 per vCPU per hour. - Key pair: choose your EC2 key pair. It is the only way in. RSA and ED25519 keys both work.
- Security group: allow inbound TCP 22 from your own IP address or network only.
- Storage: the root volume is 12 GiB gp3 by default. You can choose a larger size. The extra space is added to the LVM physical volume when you run the three commands in Step 12, because cloud-init does not grow logical volumes on its own.
- Metadata: leave the defaults, because the image already requires IMDSv2. Then choose Launch.
Option B: AWS CLI
Run this on your own workstation. Replace the AMI ID with the one shown for your Region on the Marketplace launch page. Replace the key pair, security group and subnet with your own. The root device of this AMI is /dev/xvda:
aws ec2 run-instances \
--image-id ami-0123456789abcdef0 \
--instance-type m5.large \
--key-name my-key-pair \
--security-group-ids sg-0123456789abcdef0 \
--subnet-id subnet-0123456789abcdef0 \
--block-device-mappings 'DeviceName=/dev/xvda,Ebs={VolumeSize=20,VolumeType=gp3}' \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=debian-12-lvm}]'You do not need --metadata-options: the AMI already makes IMDSv2 required.
Connect via SSH
From your workstation, connect as admin with the key pair you chose:
ssh -i ~/.ssh/my-key-pair.pem admin@<public-ip>There is no password to enter. If SSH asks for one, or says Too many authentication failures, see Troubleshooting below. The SSH server shows the login banner (/etc/issue.net) before you sign in.
You can also connect without opening port 22 at all, through AWS Systems Manager Session Manager. This requires an instance profile that allows Systems Manager. The agent is installed and running on this image.
Confirm the release
cat /etc/debian_version grep -E '^(PRETTY_NAME|NAME|VERSION_ID|VERSION|VERSION_CODENAME|ID)=' /etc/os-release uname -srmExpected output:
12.15 PRETTY_NAME="Debian GNU/Linux 12 (bookworm)" NAME="Debian GNU/Linux" VERSION_ID="12" VERSION="12 (bookworm)" VERSION_CODENAME=bookworm ID=debian Linux 6.1.0-54-cloud-amd64 x86_64This version follows Debian 12. It receives every Debian 12 point release (12.15 at build time) through normal apt updates. Your kernel version may be newer if updates have installed since this image was built.
See the LVM layout
lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINTS /dev/nvme0n1
sudo vgs
sudo lvs -o lv_name,lv_size,lv_path debian-vg findmnt -lo TARGET,SOURCE,OPTIONS -t ext4Expected output with the default 12 GiB root volume:
NAME SIZE TYPE FSTYPE MOUNTPOINTS nvme0n1 12G disk ├─nvme0n1p1 1M part ├─nvme0n1p2 200M part vfat /boot/efi ├─nvme0n1p3 1G part ext4 /boot └─nvme0n1p4 10.8G part LVM2_member ├─debian--vg-root 4G lvm ext4 / ├─debian--vg-home 1G lvm ext4 /home ├─debian--vg-tmp 1G lvm ext4 /tmp ├─debian--vg-var 1.5G lvm ext4 /var ├─debian--vg-var_tmp 512M lvm ext4 /var/tmp ├─debian--vg-var_log 1G lvm ext4 /var/log └─debian--vg-var_log_audit 1G lvm ext4 /var/log/audit VG #PV #LV #SN Attr VSize VFree debian-vg 1 7 0 wz--n- 10.80g 820.00m LV LSize Path home 1.00g /dev/debian-vg/home root 4.00g /dev/debian-vg/root tmp 1.00g /dev/debian-vg/tmp var 1.50g /dev/debian-vg/var var_log 1.00g /dev/debian-vg/var_log var_log_audit 1.00g /dev/debian-vg/var_log_audit var_tmp 512.00m /dev/debian-vg/var_tmp TARGET SOURCE OPTIONS / /dev/mapper/debian--vg-root rw,relatime /tmp /dev/mapper/debian--vg-tmp rw,nosuid,nodev,noexec,relatime /home /dev/mapper/debian--vg-home rw,nosuid,nodev,relatime /var /dev/mapper/debian--vg-var rw,nosuid,nodev,relatime /var/log /dev/mapper/debian--vg-var_log rw,nosuid,nodev,noexec,relatime /var/tmp /dev/mapper/debian--vg-var_tmp rw,nosuid,nodev,noexec,relatime /var/log/audit /dev/mapper/debian--vg-var_log_audit rw,nosuid,nodev,noexec,relatime /boot /dev/nvme0n1p3 rw,nosuid,nodev,relatime- Partitions. Partition 1 is the BIOS boot partition that GRUB uses. Partition 2 is the EFI system partition. Partition 3 is
/boot(ext4, outside LVM so that the boot loader can read it simply). Partition 4 is the LVM physical volume. - Volumes. Each logical volume is an ext4 filesystem.
/homeand/vararenodev,nosuid./tmp,/var/tmp,/var/logand/var/log/auditare alsonoexec, so nothing can be executed from them./dev/shmis mountednodev,nosuid,noexectoo. - Free space. 820 MiB of the volume group is deliberately unallocated, so you can grow whichever volume fills first without touching EBS (Step 12).
Read the compliance evidence
The evaluation the build ran after its reboot ships on the image:
ls -lh /usr/share/doc/sac/ python3 -c "import json; s=json.load(open('/usr/share/doc/sac/cis-l1-summary.json')); print('profile: ', s['profileLabel'], '(SCAP Security Guide', s['ssgVersion'] + ')'); print('scored rules: ', s['scored'], ' pass:', s['pass'], ' fail:', s['fail'], ' rate:', str(s['ratePct']) + '%'); print('waived: ', s['waived'], ' not applicable:', s['notapplicable']); print('evaluated at: ', s['evaluatedAt'])"Expected output:
total 24M -rw-r--r-- 1 root root 21M Oct 24 23:39 cis-l1-arf.xml -rw-r--r-- 1 root root 3.4M Oct 24 23:39 cis-l1-report.html -rw-r--r-- 1 root root 1.4K Oct 24 23:39 cis-l1-summary.json -rw-r--r-- 1 root root 1.9K Oct 24 23:39 cis-l1-tailoring.xml -rw-r--r-- 1 root root 2.0K Oct 24 23:39 cis-l1-waivers.txt -rw-r--r-- 1 root root 1.6K Oct 24 23:32 scap-security-guide-LICENSE.txt profile: CIS Level 1 Server (SCAP Security Guide 0.1.82) scored rules: 359 pass: 352 fail: 6 rate: 98.1% waived: 5 not applicable: 35 evaluated at: 2026-10-24T23:39:26Zcis-l1-report.htmlis the full OpenSCAP report, rule by rule. Copy it to your workstation and open it in a browser.cis-l1-arf.xmlis the same evaluation in the machine-readable ARF format, for compliance tooling.cis-l1-tailoring.xmlis the exact tailoring used, so you can re-run the same evaluation (Step 6).- The pass rate is pass / (pass + fail + error + unknown). Rules that are not applicable to this machine, not selected, or waived are not counted.
To copy the report to your workstation, run this on the workstation:
scp -i ~/.ssh/my-key-pair.pem admin@<public-ip>:/usr/share/doc/sac/cis-l1-report.html .The five waived rules
grep -v '^#' /usr/share/doc/sac/cis-l1-waivers.txt | cut -f1,2Expected output:
grub2_password [serial-console-and-boot] grub2_uefi_password [serial-console-and-boot] accounts_maximum_age_login_defs [key-only-account-aging] account_disable_post_pw_expiration [key-only-account-aging] sshd_limit_user_access [ssh-access-list]The full reason for each is the third column of the same file. In short:
- Boot loader passwords: a GRUB password would have to be baked into the image, giving every instance the same secret. Root stays locked with no password. Set your own if you need serial console access (see Step 11).
- Password aging defaults: the accounts cloud-init creates are key-only. An aging limit would expire them and lock the SSH key out. Accounts that do have a password are still aged.
- SSH AllowUsers/AllowGroups: a list fixed at build time would refuse accounts you create later. Your security group restricts who can reach port 22.
Unlike the single-volume Debian 12 version, no partition rule is waived here: the separate volumes for /tmp, /var, /var/tmp, /var/log, /var/log/audit and /home and their mount options are all scored, and pass.
The seven rules that do not pass
python3 -c "import json; [print(r) for r in json.load(open('/usr/share/doc/sac/cis-l1-summary.json'))['failingRules']]"Expected output:
file_ownerships_var_log journald_forward_to_syslog package_iptables-persistent_installed package_ufw_removed permissions_local_var_log service_systemd-journal-upload_enabled systemd_journal_upload_urlSix of them fail and one (file_ownerships_var_log) reports error. Each one is the result of a deliberate choice, and each stays in the score rather than being hidden:
Log file ownership (file_ownerships_var_log) reports error, not fail. The check accepts only files owned by root or a syslog account, and Debian has no syslog account. The one file it trips on is /var/log/aide/aide.log, which belongs to the _aide account on purpose: Debian's daily AIDE check (dailyaidecheck.service) runs as _aide and has to rotate its own log. If that file belonged to root, the daily check would fail every day and the instance would report a failed service.
ufw against the nftables and iptables package rules (package_ufw_removed, package_iptables-persistent_installed). The Debian 12 content checks for the nftables and iptables firewall branches whatever firewall you select. ufw is the image's single host firewall, so these two always fail; adding iptables-persistent would load a second, conflicting ruleset at boot.
- Login accounting files (
permissions_local_var_log). The Debian 12 check flags every file in/var/logwider than 0640 with no exceptions, which catcheswtmpandlastlog(0664) andbtmp(0660). Those are the modes login accounting needs, systemd restores them at every boot, and the CIS benchmark itself exempts them. Every other log file is 0640 or stricter. - journald forwarding (
journald_forward_to_syslog). The profile selects both "forward the journal to syslog" and "do not forward the journal to syslog", so one of the pair always fails. The image keeps the journal as the log store (ForwardToSyslog=no). rsyslog is installed and running if you want to switch: setForwardToSyslog=yesin/etc/systemd/journald.confand restartsystemd-journald. - Remote journal upload (
service_systemd-journal-upload_enabled,systemd_journal_upload_url). The benchmark wants the journal sent to a remote log server. The image cannot know yours, and with no address the upload service would fail at every boot, so it is installed but disabled. To use it, setURL=to your collector in/etc/systemd/journal-upload.conf, then runsudo systemctl enable --now systemd-journal-upload.
Re-run the evaluation yourself
Repeat the evaluation at any time with the same content and tailoring. It takes about two minutes:
sudo oscap xccdf eval \
--tailoring-file /usr/share/doc/sac/cis-l1-tailoring.xml \
--profile xccdf_co.sac_profile_cis-l1_ec2_lvm \
--results /tmp/cis-check-results.xml --report /tmp/cis-check-report.html \
/usr/share/xml/scap/ssg/content/ssg-debian12-ds.xml > /tmp/cis-check.log 2>&1 echo "oscap exit code: $? (2 = at least one rule did not pass)"
sudo python3 -c "import re,collections; c=collections.Counter(re.findall(r'<result>(\w+)</result>', open('/tmp/cis-check-results.xml').read())); s=sum(c[k] for k in ('pass','fail','error','unknown')); print(dict(sorted(c.items()))); print('pass rate: %.1f%%' % (100.0*c['pass']/s))"Expected output on a freshly launched instance:
oscap exit code: 2 (2 = at least one rule did not pass) {'error': 1, 'fail': 6, 'notapplicable': 35, 'notchecked': 4, 'notselected': 489, 'pass': 352} pass rate: 98.1%Your own report is at /tmp/cis-check-report.html. The content file is ssg-debian12-ds.xml, the Debian 12 data stream from the SCAP Security Guide 0.1.82 release (its sha256 is recorded in cis-l1-summary.json).
As you install software and change settings, re-run the evaluation to see how your changes affect the result.
Some rules are checked by small shell scripts that OpenSCAP unpacks into /tmp. Because /tmp is noexec on this version, the image includes /usr/local/bin/oscap-run-sce-script, a one-line helper that OpenSCAP calls to run those scripts through bash. Leave it in place, or those rules report error instead of pass or fail.
Confirm IMDSv2 is required
curl -s -o /dev/null -w 'IMDSv1 request without a token: HTTP %{http_code}\n' http://169.254.169.254/latest/meta-data/instance-id
TOKEN=$(curl -s -X PUT -H 'X-aws-ec2-metadata-token-ttl-seconds: 60' http://169.254.169.254/latest/api/token)
curl -s -H "X-aws-ec2-metadata-token: $TOKEN" -w '\n' http://169.254.169.254/latest/meta-data/placement/regionExpected output (your Region may differ):
IMDSv1 request without a token: HTTP 401 us-east-1A request without a session token is refused with HTTP 401, and the same request with a token works. Scripts and SDKs on the instance must use IMDSv2; every current AWS SDK and the AWS CLI do this automatically.
The host firewall and listening ports
sudo ufw status verbose
sudo ss -tlnpExpected output (process IDs vary):
Status: active Logging: on (low) Default: deny (incoming), allow (outgoing), disabled (routed) New profiles: skip To Action From -- ------ ---- Anywhere on lo ALLOW IN Anywhere 22/tcp ALLOW IN Anywhere 68/udp ALLOW IN 67/udp
# DHCP client: replies from a DHCP server (port 67) 53 on lo ALLOW IN Anywhere
# systemd-resolved loopback stub 127.0.0.54:53 (lo only) Anywhere (v6) on lo ALLOW IN Anywhere (v6) 22/tcp (v6) ALLOW IN Anywhere (v6) 53 (v6) on lo ALLOW IN Anywhere (v6)
# systemd-resolved loopback stub 127.0.0.54:53 (lo only) State Recv-Q Send-Q Local Address:Port Peer Address:PortProcess LISTEN 0 20 127.0.0.1:25 0.0.0.0:* users:(("exim4",pid=1243,fd=4)) LISTEN 0 4096 127.0.0.53%lo:53 0.0.0.0:* users:(("systemd-resolve",pid=468,fd=14)) LISTEN 0 4096 127.0.0.54:53 0.0.0.0:* users:(("systemd-resolve",pid=468,fd=16)) LISTEN 0 128 0.0.0.0:22 0.0.0.0:* users:(("sshd",pid=624,fd=3)) LISTEN 0 20 [::1]:25 [::]:* users:(("exim4",pid=1243,fd=5)) LISTEN 0 128 [::]:22 [::]:* users:(("sshd",pid=624,fd=4))- Only
sshdlistens on a network address, on port 22. The DNS stub resolver (systemd-resolved, 127.0.0.53 and 127.0.0.54) and the local mail service (exim4, 127.0.0.1 and ::1 port 25) listen on loopback only. systemd-resolved's LLMNR and multicast DNS responders are switched off. - The DHCP rule admits only replies from a DHCP server's port 67 to the instance's DHCP client, which is how the instance keeps its address. The port 53 rule applies to the loopback interface only.
- Outbound traffic is allowed, so the SSM agent, the metadata service, DNS, time sync and package updates work.
Opening a port for your own service takes two steps: allow it in the instance's security group, and allow it in ufw. For example, for a web server on 443, run on the instance:
sudo ufw allow 443/tcpIf a port is open in the security group but not in ufw (or the other way round), the traffic will not arrive.
Time sync and the Systems Manager agent
systemctl is-active chrony amazon-ssm-agent chronyc -n sourcesExpected output (the pool servers and timings vary):
active active MS Name/IP address Stratum Poll Reach LastRx Last sample =============================================================================== ^- 172.234.25.10 2 6 77 42 +62us[ +32us] +/- 71ms ^- 142.202.190.19 2 6 77 42 -1967us[-1997us] +/- 83ms ^- 23.186.168.128 2 6 77 43 +412us[ +382us] +/- 55ms ^- 69.9.131.113 2 6 77 42 +7009us[+6979us] +/- 79ms ^* 169.254.169.123 3 4 377 26 -4346ns[ -21us] +/- 530usThe ^* marks the source that chrony is using: 169.254.169.123, the Amazon Time Sync Service. It is reachable from every instance without internet access, so time stays correct in a private subnet too. The public Debian pool servers are fallbacks.
chrony replaces Debian's default systemd-timesyncd. The CIS profile wants exactly one time service, and chrony can use the Amazon Time Sync Service as its preferred source.
The AWS Systems Manager agent is installed from Amazon's own package at its current release (3.3.5538.0 on this build). It is not in the Debian archive, so apt does not update it. Systems Manager can keep it current for you (the AWS-UpdateSSMAgent document, or the Auto update SSM Agent setting in Fleet Manager).
Patch level and automatic updates
The image is fully patched at build time. Check what is pending now:
sudo apt-get update -qq apt list --upgradable 2>/dev/null cat /etc/apt/apt.conf.d/20auto-upgrades systemctl is-enabled unattended-upgrades apt-daily-upgrade.timer uname -rExpected output on an instance launched soon after the image was built (later launches list the updates released since):
Listing... APT::Periodic::Update-Package-Lists "1"; APT::Periodic::Unattended-Upgrade "1"; enabled enabled 6.1.0-54-cloud-amd64unattended-upgrades runs daily and installs updates from the Debian and Debian-Security archives automatically. To update everything now, run:
sudo apt-get update && sudo apt-get -y upgradeReboot if a new kernel was installed:
sudo systemctl rebootKernel updates rebuild the initramfs with the LVM tools included, so the instance keeps booting from its logical volumes.
Security settings on the instance
sudo passwd -S root
sudo sshd -T | grep -E '^(permitrootlogin|passwordauthentication|kbdinteractiveauthentication|pubkeyauthentication|x11forwarding|maxauthtries|clientaliveinterval|banner) '
sudo aa-status --enabled && echo "AppArmor: enabled"
sudo aa-status | grep -E '^[0-9]+ profiles are (loaded|in enforce mode|in complain mode)' grep -c nullok /etc/pam.d/common-auth || true systemctl is-system-runningExpected output:
root L 2026-10-21 0 99999 7 -1 maxauthtries 4 clientaliveinterval 300 permitrootlogin no pubkeyauthentication yes passwordauthentication no kbdinteractiveauthentication no x11forwarding no banner /etc/issue.net AppArmor: enabled 12 profiles are loaded. 12 profiles are in enforce mode. 0 profiles are in complain mode. 0 runningLmeans the root account is locked.maxauthtries 4andclientaliveinterval 300come from the CIS profile: SSH allows four authentication attempts per connection and checks idle sessions every five minutes.- AppArmor is enabled, with every loaded profile in enforce mode (12 loaded, 12 enforce, 0 complain on this build).
0means PAM no longer accepts an empty password (nullokremoved frompam_unix).runningmeans no service failed during boot.
EC2 Serial Console and recovery. No account on this image has a password, so the serial console cannot log in until you set one. If you want serial console access, set a password for your own user on your running instance. The password is yours and is not on the image:
sudo passwd adminGrowing a volume, swap and file integrity
sudo vgs debian-vg df -h / /var /var/log
sudo grep -E 'cc_growpart.py.*(SKIPPED|resized|NOCHANGE)' /var/log/cloud-init.log | tail -1 | cut -c1-140 sudo swapon --noheadings --show | wc -l systemctl is-enabled aidecheck.timer grep -E '^(ExecStart|SuccessExitStatus)=' /etc/systemd/system/aidecheck.serviceExpected output with the default 12 GiB root volume:
VG #PV #LV #SN Attr VSize VFree debian-vg 1 7 0 wz--n- 10.80g 820.00m Filesystem Size Used Avail Use% Mounted on /dev/mapper/debian--vg-root 3.9G 1.1G 2.7G 29% / /dev/mapper/debian--vg-var 1.5G 269M 1.1G 20% /var /dev/mapper/debian--vg-var_log 974M 17M 890M 2% /var/log 2026-10-24 23:57:04,941 - cc_growpart.py[DEBUG]: '/' SKIPPED: Resizing mapped device (/dev/mapper/debian--vg-root) skipped as it is not encr 0 enabled ExecStart=/usr/bin/aide --config /etc/aide/aide.conf --check SuccessExitStatus=1 2 3 4 5 6 7cloud-init does not grow LVM volumes. On the single-volume Debian image, cloud-init grows the root filesystem to the volume size on first boot. On this image the root filesystem is a logical volume, so cloud-init skips it (the SKIPPED line above). That is deliberate: you decide which volume gets the space.
When you launch with a bigger root volume, or grow the EBS volume later with aws ec2 modify-volume, add the space in three steps on the instance:
- Grow partition 4 (the LVM physical volume) to the end of the disk, and tell LVM:
sudo growpart /dev/nvme0n1 4
sudo pvresize /dev/nvme0n1p4- Give space to the volumes that need it.
--resizefsgrows the filesystem in the same step, online, with no reboot:
sudo lvextend --resizefs -L +4G /dev/debian-vg/root
sudo lvextend --resizefs -L +2G /dev/debian-vg/var- Check the result:
sudo vgs debian-vg df -h / /varExample output after growing a 12 GiB volume to 20 GiB and running the commands above:
CHANGED: partition=4 start=2510848 old: size=22654943 end=25165790 new: size=39432159 end=41943006 Physical volume "/dev/xvda4" changed 1 physical volume(s) resized or updated / 0 physical volume(s) not resized Size of logical volume debian-vg/root changed from 4.00 GiB (1024 extents) to 8.00 GiB (2048 extents). Logical volume debian-vg/root successfully resized. Size of logical volume debian-vg/var changed from 1.50 GiB (384 extents) to 3.50 GiB (896 extents). Logical volume debian-vg/var successfully resized. VG #PV #LV #SN Attr VSize VFree debian-vg 1 7 0 wz--n- 18.80g 2.80g Filesystem Size Used Avail Use% Mounted on /dev/mapper/debian--vg-root 7.8G 1.1G 6.4G 14% / /dev/mapper/debian--vg-var 3.5G 46M 3.2G 2% /varTo give a volume all the remaining free space instead, use -l +100%FREE, for example sudo lvextend --resizefs -l +100%FREE /dev/debian-vg/var_log.
Leaving some space unallocated is useful, because you can grow whichever volume fills first without touching EBS. Volumes can grow online but cannot shrink online, so add space in steps.
- Swap. The image has no swap. If your workload needs it, create a logical volume for it (for example
sudo lvcreate -L 2G -n swap debian-vg, thensudo mkswap /dev/debian-vg/swapand an/etc/fstabentry), or a swap file on/or/var. - AIDE. The file integrity database is built from the image's own files. The CIS profile runs
aide --checkat boot and daily at 05:00 (aidecheck.timer); differences are reported in the journal (journalctl -u aidecheck), and a real AIDE error marks the unit failed. On a new instance a check may report files cloud-init creates for that instance (such as its hostname); after you have reviewed the report, refresh the database withsudo aide --config /etc/aide/aide.conf --initand move the new database into place (sudo mv /var/lib/aide/aide.db.new /var/lib/aide/aide.db). After you grow a volume, AIDE also reports the new LVM metadata backups in/etc/lvm. Debian's own daily check (dailyaidecheck.timer) also runs, as the_aideaccount, and writes/var/log/aide/aide.log; that is why the AIDE log files belong to_aide.
Install software and add users
The instance uses Debian's own archive (bookworm, bookworm-updates, bookworm-backports and bookworm-security, through the mirror list Debian's cloud images ship in /etc/apt/mirrors/):
apt-cache policy nginx | sed -n 1,3pExpected output (the version changes over time):
nginx: Installed: (none) Candidate: 1.22.1-9+deb12u10Install and start software in the usual way, for example sudo apt-get -y install nginx then sudo systemctl enable --now nginx, and open its port in the security group and ufw (Step 8). The AWS CLI version 2 is on the image as Debian's own awscli package (aws --version reports aws-cli/2.9.19); it uses IMDSv2 and the instance profile automatically, and apt keeps it at Debian 12's release of the package. If you need a newer release, install it from AWS (AWS CLI install guide), which places it in /usr/local/bin, ahead of /usr/bin on the PATH. Put application data on a volume that suits it: /var for services that keep state under /var/lib, or a new logical volume of your own (sudo lvcreate -L 2G -n data debian-vg) if you leave space unallocated for it.
To add another administrator, create the account, add it to the sudo group and give it an SSH key:
sudo useradd -m -s /bin/bash -G sudo alice
sudo install -d -m 700 -o alice -g alice /home/alice/.ssh echo "ssh-ed25519 AAAA... alice@laptop" | sudo tee /home/alice/.ssh/authorized_keys
sudo chown alice:alice /home/alice/.ssh/authorized_keys && sudo chmod 600 /home/alice/.ssh/authorized_keysBecause SSH is key-only, a new account needs its key in authorized_keys. The new account has no password, so sudo cannot authenticate it until you give it one with sudo passwd alice. SSH stays key-only, and sudo uses the password.
Password-free sudo is granted only to admin, created by cloud-init, and to ssm-user, the account that Session Manager creates for its sessions.
Common use cases
- Workloads that must show CIS Level 1 alignment on Debian including the partitioning rules, with the evaluation report already on the machine
- Servers whose logs, temporary files or application data must not be able to fill the root filesystem
- Hosts that need to grow one area (logs,
/var/lib, home directories) without resizing everything - Fleets managed through Systems Manager, with IMDSv2 required from the first boot
- A starting point for your own golden image: add your software, re-run Step 6, and keep the report with your build
Troubleshooting
SSH says "Too many authentication failures"
The SSH server allows four authentication attempts per connection. If your SSH agent holds several keys, it can use them up before it offers the right one. Tell SSH to offer only the key you name:
ssh -o IdentitiesOnly=yes -i ~/.ssh/my-key-pair.pem admin@<public-ip>Cannot connect via SSH
- Check the instance is running and has passed its status checks.
- Check the security group allows TCP 22 from your current public address.
- Use the user name
admin(notdebian,ec2-userorroot) and the key pair chosen at launch. Password login is disabled, so a wrong key never falls back to a password prompt. - If you changed ufw, make sure 22/tcp is still allowed (
sudo ufw allow 22/tcp). Session Manager (Step 2) still works if SSH is blocked, provided the instance has a Systems Manager instance profile.
I launched with a bigger volume but df shows the same size
That is expected on this image: cloud-init does not grow logical volumes. Follow Step 12 to grow the physical volume and then the logical volumes you choose.
A volume is full
Check which one with df -h. If the volume group has free space (sudo vgs), grow the full volume with lvextend --resizefs (Step 12). If not, grow the EBS volume first with aws ec2 modify-volume or the console, then run the three steps in Step 12.
"Permission denied" running a script or binary
/tmp, /var/tmp, /var/log and /var/log/audit are mounted noexec. Run scripts through their interpreter (bash script.sh) or move them to your home directory or /opt.
My service is running but not reachable
Allow the port in both the security group and ufw (Step 8). sudo ufw status verbose shows the ufw rules; sudo ss -tlnp shows whether the service listens on all addresses or only on loopback.
A tool that uses instance metadata fails
The image requires IMDSv2. Old scripts that call http://169.254.169.254/ without a token get HTTP 401. Update them to request a token first (Step 7), or use the AWS CLI or a current SDK.
Running the daily AIDE check by hand a second time fails
Debian's dailyaidecheck.service (run once a day by dailyaidecheck.timer) allows one start every two hours, and its cache helper dailyaidecheck-buildcache.service one a minute. A second sudo systemctl start dailyaidecheck.service inside that window is refused (start-limit-hit or A dependency job for dailyaidecheck.service failed), and systemctl is-system-running then reports degraded. Nothing is wrong with AIDE: wait, then clear the record with sudo systemctl reset-failed, which returns the state to running.
Package manager issues
Refresh the package lists first:
sudo apt-get update -qq && echo "package lists refreshed"If the archive is unreachable, check that the instance has outbound internet access (a public IP or a NAT gateway) and that DNS resolves:
getent hosts deb.debian.orgImportant notes
Debian is free and open-source software. It is a distribution of thousands of packages, each under its own license (GPL, LGPL, MIT, BSD, Apache 2.0 and others). /usr/share/doc/<package>/copyright shows each package's license. No subscription or license key is needed to run this image.
The compliance tooling is OpenSCAP (LGPL 2.1 or later) and SCAP Security Guide content (BSD 3-Clause, license text in /usr/share/doc/sac/scap-security-guide-LICENSE.txt).
Debian 12 left regular security support in June 2026 and receives security updates from Debian LTS until June 2028, through the same bookworm-security archive this image already uses. This version tracks the Debian 12 release stream, so new point releases arrive as ordinary updates.
The CIS Level 1 Server profile used here is the OpenSCAP profile published in SCAP Security Guide. These images are not CIS Hardened Images and are not certified by the Center for Internet Security. A pass rate measures this image as built; your own changes affect it, which is why the evaluation can be re-run (Step 6).
Debian is a registered trademark of Software in the Public Interest, Inc. CIS is a trademark of the Center for Internet Security. These names are used only to identify the software and the profile. SecureAnyCloud is not affiliated with or endorsed by the Debian Project, Software in the Public Interest or the Center for Internet Security.
Support
For help with this image, contact SecureAnyCloud support:
- Website: secureanycloud.com
- User Guides: secureanycloud.com/guides
- Debian on AWS hub: secureanycloud.com/guides/debian-server-aws
Deploy on AWS
Launch Debian 12 LVM on AWS with 24/7 support from SecureAnyCloud.
View on Marketplace
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com