This guide deploys VidIO from the current local/Kubernetes state to AWS with:
https://vidio.domain.comfor the Angular portal and Keycloak login routes.https://api.vidio.domain.comfor API traffic.- Route 53 managing only the delegated
vidio.domain.comsubdomain. - Spaceship continuing to manage the root
domain.com. - EKS for the application workloads.
- S3 for video assets.
- SES for Keycloak signup verification email.
- ACM for HTTPS.
- ALB Ingress for public traffic.
Replace domain.com with your real root domain throughout.
Install and authenticate:
- AWS CLI
- Docker Desktop
- Terraform
kubectl- Helm
Check AWS access:
aws sts get-caller-identity
aws configure get regionSet local variables:
$AWS_REGION="us-east-1"
$ROOT_DOMAIN="domain.com"
$ZONE_NAME="vidio.domain.com"
$APP_HOST="vidio.domain.com"
$API_HOST="api.vidio.domain.com"
$CLUSTER_NAME="vidio-dev"
$PROJECT="vidio"
$ACCOUNT_ID=(aws sts get-caller-identity --query Account --output text)
$ECR="$ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com"Copy the example:
Copy-Item infrastructure/terraform/envs/dev/terraform.tfvars.example infrastructure/terraform/envs/dev/terraform.tfvarsEdit infrastructure/terraform/envs/dev/terraform.tfvars:
aws_region = "us-east-1"
project_name = "vidio"
cluster_name = "vidio-dev"
video_bucket_name = "vidio-domain-com-videos-dev"
route53_zone_name = "vidio.domain.com"
app_hostname = "vidio.domain.com"
api_hostname = "api.vidio.domain.com"
ses_email_identity = "no-reply@vidio.domain.com"
ses_domain_identity = "vidio.domain.com"
# Fill these after the ALB exists.
alb_dns_name = ""
alb_zone_id = ""
node_instance_types = ["t3.small"]
node_desired_size = 3
node_min_size = 2
node_max_size = 3The bucket name must be globally unique. If this one is taken, add your initials or account id.
Because your root domain is managed by Spaceship, first create only the delegated subdomain hosted zone:
terraform -chdir=infrastructure/terraform/envs/dev init
terraform -chdir=infrastructure/terraform/envs/dev apply -target=aws_route53_zone.vidioGet the Route 53 nameservers:
terraform -chdir=infrastructure/terraform/envs/dev output route53_name_serversIn Spaceship DNS for domain.com, add an NS record:
Type: NS
Host/Name: vidio
Value: the 4 Route 53 nameservers from terraform output
TTL: automatic or 300
This delegates only vidio.domain.com to AWS. It does not move the whole root domain.
Wait for delegation:
nslookup -type=NS vidio.domain.comThe answer should show the Route 53 nameservers.
Now run the full Terraform apply:
terraform -chdir=infrastructure/terraform/envs/dev fmt
terraform -chdir=infrastructure/terraform/envs/dev validate
terraform -chdir=infrastructure/terraform/envs/dev plan
terraform -chdir=infrastructure/terraform/envs/dev applyTerraform provisions:
- VPC and public subnets
- EKS cluster and managed node group
- EBS CSI add-on
- S3 bucket for video assets
- Route 53 hosted zone records for ACM and SES
- ACM certificate for
vidio.domain.comandapi.vidio.domain.com - SES domain identity, DKIM records, and sender email identity
- IRSA roles for
video-serviceandprocessing-service - IAM role/policy for AWS Load Balancer Controller
Save outputs:
terraform -chdir=infrastructure/terraform/envs/dev outputYou need:
video_bucket_name
route53_name_servers
acm_certificate_arn
video_service_role_arn
processing_service_role_arn
alb_controller_role_arn
If ACM or SES validation is slow, wait a few minutes and run terraform apply again.
SES may start in sandbox mode. In sandbox mode, verification emails can only be sent to verified recipients.
For real public signup:
- Open AWS SES in
us-east-1. - Go to Account dashboard.
- Request production access.
- Use case: transactional account verification emails for VidIO.
SMTP details for Keycloak:
SMTP host: email-smtp.us-east-1.amazonaws.com
SMTP port: 587
From: no-reply@vidio.domain.com
SMTP username: generated in SES
SMTP password: generated in SES
Create SMTP credentials from:
SES -> SMTP settings -> Create SMTP credentials
aws eks update-kubeconfig --region $AWS_REGION --name $CLUSTER_NAME
kubectl get nodesWait until nodes are Ready.
aws ecr create-repository --repository-name vidio-api-service --region $AWS_REGION
aws ecr create-repository --repository-name vidio-video-service --region $AWS_REGION
aws ecr create-repository --repository-name vidio-processing-service --region $AWS_REGION
aws ecr create-repository --repository-name vidio-admin-dashboard --region $AWS_REGIONIf a repository already exists, ignore the RepositoryAlreadyExistsException.
Login Docker to ECR:
aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin $ECRThe local portal uses localhost. For AWS, edit admin-dashboard/src/main.ts:
const apiBaseUrl = isLocalBrowser ? 'http://localhost:8081/api' : 'https://api.vidio.domain.com/api';
const keycloakUrl = isLocalBrowser ? 'http://localhost:8089' : 'https://vidio.domain.com';Replace with your real domain if different.
docker build -t vidio-api-service ./new-services/api-service
docker build -t vidio-video-service ./new-services/video-service
docker build -t vidio-processing-service ./new-services/processing-service
docker build -t vidio-admin-dashboard ./admin-dashboardTag:
docker tag vidio-api-service:latest "$ECR/vidio-api-service:latest"
docker tag vidio-video-service:latest "$ECR/vidio-video-service:latest"
docker tag vidio-processing-service:latest "$ECR/vidio-processing-service:latest"
docker tag vidio-admin-dashboard:latest "$ECR/vidio-admin-dashboard:latest"Push:
docker push "$ECR/vidio-api-service:latest"
docker push "$ECR/vidio-video-service:latest"
docker push "$ECR/vidio-processing-service:latest"
docker push "$ECR/vidio-admin-dashboard:latest"Get values:
$ALB_ROLE_ARN=(terraform -chdir=infrastructure/terraform/envs/dev output -raw alb_controller_role_arn)
$VPC_ID=(aws eks describe-cluster --name $CLUSTER_NAME --region $AWS_REGION --query "cluster.resourcesVpcConfig.vpcId" --output text)Install:
helm repo add eks https://aws.github.io/eks-charts
helm repo update
helm upgrade --install aws-load-balancer-controller eks/aws-load-balancer-controller `
-n kube-system `
--set clusterName=$CLUSTER_NAME `
--set region=$AWS_REGION `
--set vpcId=$VPC_ID `
--set serviceAccount.create=true `
--set serviceAccount.name=aws-load-balancer-controller `
--set serviceAccount.annotations."eks\.amazonaws\.com/role-arn"=$ALB_ROLE_ARNVerify:
kubectl get deployment -n kube-system aws-load-balancer-controllerEdit k8s/aws/config.yaml:
S3_REGION: us-east-1
S3_BUCKET: <terraform video_bucket_name>
S3_ENDPOINT: ""
S3_PUBLIC_ENDPOINT: ""
S3_ACCESS_KEY: ""
S3_SECRET_KEY: ""
S3_PATH_STYLE_ACCESS: "false"
S3_PRESIGNED_URL_EXPIRATION_MINUTES: "10"
VIDIO_SMTP_HOST: email-smtp.us-east-1.amazonaws.com
VIDIO_SMTP_PORT: "587"
VIDIO_SMTP_FROM: no-reply@vidio.domain.com
VIDIO_SMTP_USER: <ses-smtp-username>
VIDIO_SMTP_PASSWORD: <ses-smtp-password>
VIDIO_SMTP_AUTH: "true"
VIDIO_SMTP_STARTTLS: "true"Edit k8s/aws/serviceaccounts.yaml:
eks.amazonaws.com/role-arn: <video_service_role_arn>
eks.amazonaws.com/role-arn: <processing_service_role_arn>Edit image names in k8s/aws/services.yaml:
<account>.dkr.ecr.us-east-1.amazonaws.com/vidio-api-service:latest
<account>.dkr.ecr.us-east-1.amazonaws.com/vidio-video-service:latest
<account>.dkr.ecr.us-east-1.amazonaws.com/vidio-processing-service:latest
<account>.dkr.ecr.us-east-1.amazonaws.com/vidio-admin-dashboard:latest
Edit k8s/aws/ingress.yaml:
- Replace
replace-with-terraform-acm-certificate-arnwith Terraform outputacm_certificate_arn. - Replace
vidio.domain.comandapi.vidio.domain.comif your actual hostnames differ.
Edit k8s/aws/keycloak.yaml if needed so the dashboard client contains:
"redirectUris": [
"https://vidio.domain.com/*",
"http://localhost:8088/*",
"http://localhost:4200/*"
],
"webOrigins": [
"https://vidio.domain.com",
"https://api.vidio.domain.com",
"+"
]kubectl apply -k k8s/awsWatch rollout:
kubectl get pods -n vidio -wCheck services and ingress:
kubectl get svc -n vidio
kubectl get ingress -n vidioWait until ingress has an address like:
k8s-vidio-xxxxxxxx.us-east-1.elb.amazonaws.com
Get the ALB DNS name:
$ALB_DNS=(kubectl get ingress vidio -n vidio -o jsonpath="{.status.loadBalancer.ingress[0].hostname}")
$ALB_DNSGet the ALB canonical hosted zone id:
$ALB_NAME=($ALB_DNS -split "\.")[0]
$ALB_ZONE_ID=(aws elbv2 describe-load-balancers --region $AWS_REGION --query "LoadBalancers[?DNSName=='$ALB_DNS'].CanonicalHostedZoneId" --output text)
$ALB_ZONE_IDEdit infrastructure/terraform/envs/dev/terraform.tfvars:
alb_dns_name = "k8s-vidio-xxxxxxxx.us-east-1.elb.amazonaws.com"
alb_zone_id = "Zxxxxxxxxxxxxx"Apply Terraform again:
terraform -chdir=infrastructure/terraform/envs/dev applyTerraform now creates Route 53 alias records:
vidio.domain.com -> ALB
api.vidio.domain.com -> ALB
Wait for DNS:
nslookup vidio.domain.com
nslookup api.vidio.domain.comAPI health:
curl.exe -i https://api.vidio.domain.com/healthOpen:
https://vidio.domain.com
Test:
- Sign up with a real email address.
- Open the verification email from SES.
- Log in.
- Upload a small
.mp4. - Wait for status
COMPLETED. - Open original, thumbnail, and processed assets.
Check S3:
aws s3 ls s3://<video_bucket_name>/ --recursiveCheck logs:
kubectl logs -n vidio deployment/api-service
kubectl logs -n vidio deployment/video-service
kubectl logs -n vidio deployment/processing-service
kubectl logs -n vidio statefulset/kafkaKeycloak admin:
https://vidio.domain.com/admin
Initial local/dev admin credentials:
admin / admin
Change them before sharing the deployment.
If https://vidio.domain.com does not resolve:
- Confirm Spaceship has an
NSrecord for hostvidio. - Confirm it points to the Route 53 hosted zone nameservers.
- Run
nslookup -type=NS vidio.domain.com.
If HTTPS fails:
- Confirm ACM certificate is
Issued. - Confirm
k8s/aws/ingress.yamlhas the correct certificate ARN. - Confirm the ALB listener has HTTPS 443.
If login redirects fail:
- Check Keycloak client redirect URIs.
- Check frontend
keycloakUrl. - Check
/realmsand/resourcesroute to Keycloak through the ALB.
If API returns 401:
- Keep
KEYCLOAK_JWK_SET_URIinternal:http://keycloak:8080/realms/vidio/protocol/openid-connect/certs
If presigned S3 URLs fail:
- Ensure
S3_ENDPOINT,S3_PUBLIC_ENDPOINT,S3_ACCESS_KEY, andS3_SECRET_KEYare empty in AWS. - Ensure
S3_PATH_STYLE_ACCESSis"false". - Check IRSA role annotations on service accounts.
Delete Kubernetes workloads:
kubectl delete -k k8s/awsEmpty S3 bucket:
aws s3 rm s3://<video_bucket_name> --recursiveDestroy Terraform:
terraform -chdir=infrastructure/terraform/envs/dev destroyRemove the vidio NS record from Spaceship if you no longer want to delegate the subdomain.