Kheeper

Boot on AWS

This guide walks through booting your first Kheeper-managed host on Amazon Web Services.

Prerequisites

Set the variables in your shell needed for the remaining steps.

ORG=$(kheeper orgs list | grep 'user-[0-9a-f]' | awk '{ print $1 }')
ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
AWS_REGION=us-east-2

Note the us-east-2 region is currently the only region that has the Kheeper API so instances must be launched in that region.

Step 1 — Create an IAM role for EC2

The instance needs an IAM role so it can call STS GetCallerIdentity during auto-registration. Create a minimal role:

aws iam create-role \
  --role-name kheeper-autoregister \
  --assume-role-policy-document '{
    "Version": "2012-10-17",
    "Statement": [{
      "Effect": "Allow",
      "Principal": {"Service": "ec2.amazonaws.com"},
      "Action": "sts:AssumeRole"
    }]
  }'

aws iam create-instance-profile \
  --instance-profile-name kheeper-autoregister

aws iam add-role-to-instance-profile \
  --instance-profile-name kheeper-autoregister \
  --role-name kheeper-autoregister

Or, equivalently, with Terraform:

resource "aws_iam_role" "kheeper_autoregister" {
  name = "kheeper-autoregister"

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Effect    = "Allow"
      Principal = { Service = "ec2.amazonaws.com" }
      Action    = "sts:AssumeRole"
    }]
  })
}

resource "aws_iam_instance_profile" "kheeper_autoregister" {
  name = "kheeper-autoregister"
  role = aws_iam_role.kheeper_autoregister.name
}

No additional permissions are needed — GetCallerIdentity requires no IAM permissions.

Step 2 — Connect your AWS account

Link your AWS account to your Kheeper org so that AWS instances can auto-register as Kheeper hosts.

kheeper clouds create ${ORG}/my-aws --provider aws --account-id ${ACCOUNT_ID}

This will allow new instances created from Kheeper AMIs to register as new hosts in the region shown by kheeper status. Kheeper accounts are region-scoped. If the same AWS account is linked to multiple Kheeper regions, instances will fail to register unless a region is configured with metadata:

aws ec2 run-instances --user-data 'kheeper-region=us.kheeper.com'

Step 3 — Create an EC2 instance

Since this guide uses the postgres image, create a security group that will allow your instance to get a TLS certificate from Lets Encrypt (port 80) and listen for incoming connections to postgres (port 5432). Additionally ssh (port 22) allows for accessing the instance for sysadmin work.

VPC_ID=$(aws ec2 describe-vpcs --filters "Name=isDefault,Values=true" \
  --query 'Vpcs[0].VpcId' --output text --region ${AWS_REGION})

SG_ID=$(aws ec2 create-security-group \
  --group-name kheeper-http-postgres \
  --description "Allow HTTP and Postgres" \
  --vpc-id ${VPC_ID} \
  --region ${AWS_REGION} \
  --query 'GroupId' --output text)

aws ec2 authorize-security-group-ingress \
  --group-id ${SG_ID} \
  --protocol tcp --port 80 --cidr 0.0.0.0/0 \
  --region ${AWS_REGION}

aws ec2 authorize-security-group-ingress \
  --group-id ${SG_ID} \
  --protocol tcp --port 5432 --cidr 0.0.0.0/0 \
  --region ${AWS_REGION}

aws ec2 authorize-security-group-ingress \
  --group-id ${SG_ID} \
  --protocol tcp --port 22 --cidr 0.0.0.0/0 \
  --region ${AWS_REGION}

Then launch an instance using the public Kheeper AMI. This is a Fedora bootc image that automatically connects to the Kheeper registry on first boot.

AMI_ID=$(aws ec2 describe-images \
  --owners 223249276674 \
  --filters "Name=name,Values=kheeper-aws-*" "Name=state,Values=available" \
  --query 'sort_by(Images, &CreationDate)[-1].ImageId' \
  --output text --region ${AWS_REGION})

INSTANCE_ID=$(aws ec2 run-instances \
  --image-id ${AMI_ID} \
  --instance-type m7i.large \
  --block-device-mappings '[{"DeviceName":"/dev/sda1","Ebs":{"VolumeSize":40}}]' \
  --iam-instance-profile Name=kheeper-autoregister \
  --security-group-ids ${SG_ID} \
  --user-data 'kheeper-region=us.kheeper.com' \
  --region ${AWS_REGION} \
  --query 'Instances[0].InstanceId' --output text)

echo "Launched ${INSTANCE_ID}"

Or, equivalently, with Terraform (the region comes from your aws provider block):

data "aws_vpc" "default" {
  default = true
}

resource "aws_security_group" "kheeper_http_postgres" {
  name        = "kheeper-http-postgres"
  description = "Allow HTTP and Postgres"
  vpc_id      = data.aws_vpc.default.id
}

resource "aws_vpc_security_group_ingress_rule" "http" {
  security_group_id = aws_security_group.kheeper_http_postgres.id
  ip_protocol       = "tcp"
  from_port         = 80
  to_port           = 80
  cidr_ipv4         = "0.0.0.0/0"
}

resource "aws_vpc_security_group_ingress_rule" "postgres" {
  security_group_id = aws_security_group.kheeper_http_postgres.id
  ip_protocol       = "tcp"
  from_port         = 5432
  to_port           = 5432
  cidr_ipv4         = "0.0.0.0/0"
}

resource "aws_vpc_security_group_ingress_rule" "ssh" {
  security_group_id = aws_security_group.kheeper_http_postgres.id
  ip_protocol       = "tcp"
  from_port         = 22
  to_port           = 22
  cidr_ipv4         = "0.0.0.0/0"
}

data "aws_ami" "kheeper" {
  owners      = ["223249276674"]
  most_recent = true

  filter {
    name   = "name"
    values = ["kheeper-aws-*"]
  }

  filter {
    name   = "state"
    values = ["available"]
  }
}

resource "aws_instance" "kheeper" {
  ami                    = data.aws_ami.kheeper.id
  instance_type          = "m7i.large"
  iam_instance_profile   = aws_iam_instance_profile.kheeper_autoregister.name
  vpc_security_group_ids = [aws_security_group.kheeper_http_postgres.id]
  user_data              = "kheeper-region=us.kheeper.com"

  root_block_device {
    volume_size = 40
  }
}

Step 4 — Verify Host Registration

The instance will boot and auto-register within a few minutes. Check that the host appeared:

kheeper hosts list --org ${ORG}

You should see a host whose instance name matches the instance ID you launched.

Step 5 — Author the config

Generate a starter config from the image's schema and edit it to fit your host. Here we are using the public postgres image: You can use the DNS name from kheeper hosts list where the config requests a domain.

kheeper releases start config.json --image us.kheeper.com/public/postgres:v0.2.0

Here's an example for a config with a single ssh public key that creates a database named kheeper and a database user named kheeper.

{
  "base": {
    "admin_authorized_keys": "ssh-ed25519 AAAAC3NzaC1lZDI1NET5AAAAINFEt8Q1O8VSKe+GVOsHMqjXdyukUkM38VI/edNs0ur8"
  },
  "database": {
    "name": "kheeper",
    "users": [{"name": "kheeper", "password": "changeme1234567890"}]
  },
  "postgres": {
    "domain_name": "i-0a35818c9f09bada3.user-12345678.us.kheeper.app"
  }
}

Step 6 — Create a release

Create and activate a release that pairs the image with your config:

kheeper releases create ${ORG}/${INSTANCE_ID}:v1 \
  --image us.kheeper.com/public/postgres:v0.2.0 \
  --config-file config.json \
  --activate

Step 7 — Verify Postgres

The host will pick up the release within a minute, pull and reboot.

You can now connect to your Postgres server with psql.

HOST_DNS=$INSTANCE_ID.user-12345678.us.kheeper.app
USER=kheeper
export PGPASSWORD=changeme1234567890

psql "host=$HOST_DNS user=$USER sslmode=verify-full sslrootcert=system"

Troubleshooting

If an instance fails to autoregister as a new host or fails to reboot into a release after you activate it, you should inspect the console output:

aws ec2 get-console-output --instance-id ${INSTANCE_ID} --region ${AWS_REGION} --output text

Clean up

Terminate the EC2 instance and delete the Kheeper host when you're done:

aws ec2 terminate-instances --instance-ids ${INSTANCE_ID} --region ${AWS_REGION}
kheeper hosts delete ${ORG}/${INSTANCE_ID}