Skip to main content

Jenkins on MiniStack

Deploy Jenkins on Fargate locally, query MiniStack with the AWS CLI, and retrieve the initial admin password the same way you would on AWS (CloudWatch Logs) — plus local fallbacks.

See also: Setup · Deployment · Verification · Advanced Configuration · Troubleshooting


What you get locally​

A DEV Jenkins Fargate stack (VPC, ALB, ECS, IAM, CloudWatch log group) adapted for MiniStack:

AWS (canonical)MiniStack (adapted)
EFS for /var/jenkins_homeHost bind mount under .ministack-volumes/
ALB forward → ECSALB redirect → http://localhost:<port>
Application Auto ScalingRemoved (not emulated)
CloudWatch Logs (awslogs)Supported — primary path for bootstrap output

Naming (from stackName in deployment-context.json):

ConceptPatternExample (stackName=my-jenkins)
MiniStack CloudFormation stack<stackName>-ministackmy-jenkins-ministack
Canonical templatecdk.out/<stackName>.template.jsoncdk.out/my-jenkins.template.json
CloudWatch log group/aws/ecs/<stackName>/fargate/<securityProfile>/aws/ecs/my-jenkins/fargate/dev
Host bind mount.ministack-volumes/<stackName>/jenkinsHome.ministack-volumes/my-jenkins/jenkinsHome
Log stream prefix<applicationId>/...jenkins/...

1. Prerequisites and build​

Same as Setup. From the repository root:

mvn clean install -DskipTests
mvn -f cfc-testing package -Dmaven.test.skip=true

# Start MiniStack (ministack → start)
cd cfc-testing
java -cp "target/classes:target/dependency/*" \
com.cloudforgeci.samples.app.InteractiveDeployer --platform
curl -s http://localhost:4566/_ministack/health

Save as cfc-testing/deployment-context.json for a minimal Jenkins Fargate deploy (no domain, auth, or autoscaling). Choose any valid stackName — it drives log groups, templates, and volume paths.

{
"stackName": "my-jenkins",
"applicationId": "jenkins",
"environment": "dev",
"runtime": "FARGATE",
"topology": "application-service",
"securityProfile": "DEV",
"networkMode": "public",
"enableSsl": false,
"authMode": "none",
"cpu": 1024,
"memory": 2048,
"minInstanceCapacity": 1,
"maxInstanceCapacity": 1,
"enableAutoScaling": false,
"wafEnabled": false,
"cloudfrontEnabled": false,
"enableMonitoring": false,
"complianceFrameworks": [],
"complianceMode": "DISABLED",
"region": "us-east-1"
}
FieldValueWhy
stackNameyour choice (e.g. my-jenkins)Used for templates, log group, bind-mount path
enableAutoScalingfalseApplication Auto Scaling is stripped locally anyway
minInstanceCapacity / maxInstanceCapacity1 / 1Single task — avoids port conflicts on :8080
domain / authModeomitted / noneSimplest path for first deploy
wafEnabled / complianceFrameworksfalse / []WAF and compliance resources are blocked by MiniStack preflight

3. Deploy​

cd cfc-testing
java -cp "target/classes:target/dependency/*" \
com.cloudforgeci.samples.app.InteractiveDeployer
# Choose 6 (Deploy to MiniStack)

With deployment-context.json present, the deployer skips prompts and shows the menu; choose 6.

Expected artifacts in cdk.out/ (replace <stackName> with your value):

  • <stackName>.template.json: canonical AWS template
  • <stackName>.ministack.template.json: adapted template deployed to MiniStack
  • <stackName>.ministack-adaptations.json: audit of local changes

Expected stack outputs:

OutputExampleUse
MiniStackApplicationUrlhttp://localhost:8080Open Jenkins in browser
MiniStackLocalUrlhttp://localhost:4566/_alb/<name>/ALB entry (redirects to app port)
MiniStackHostVolumeJenkinsHome.../.ministack-volumes/<stackName>/jenkinsHomePersistent home directory on host

4. Shell variables (use throughout)​

Set these once per terminal session from your deployment-context.json:

# Required — must match stackName in deployment-context.json
export STACK_NAME=my-jenkins

# Derived — used in AWS CLI examples below
export MINISTACK_STACK="${STACK_NAME}-ministack"
export LOG_GROUP="/aws/ecs/${STACK_NAME}/fargate/dev"
export VOLUME_HOME=".ministack-volumes/${STACK_NAME}/jenkinsHome"

5. Configure AWS CLI for MiniStack​

MiniStack accepts any credentials. Point every AWS CLI call at http://localhost:4566.

Option A — Shell exports (quick session)​

export AWS_ACCESS_KEY_ID=test
export AWS_SECRET_ACCESS_KEY=test
export AWS_DEFAULT_REGION=us-east-1
export AWS_ENDPOINT_URL=http://localhost:4566

With AWS_ENDPOINT_URL set (AWS CLI v2.13+), you do not need --endpoint-url on each command:

aws cloudformation describe-stacks --stack-name "$MINISTACK_STACK"
aws logs describe-log-groups --log-group-name-prefix "/aws/ecs/${STACK_NAME}"

Option B — Named profile (persistent)​

~/.aws/credentials:

[ministack]
aws_access_key_id = test
aws_secret_access_key = test

~/.aws/config:

[profile ministack]
region = us-east-1
output = json
endpoint_url = http://localhost:4566

Use it:

aws --profile ministack cloudformation describe-stacks --stack-name "$MINISTACK_STACK"

Prefer AWS_ENDPOINT_URL over per-command --endpoint-url so the AWS CLI and the Java deployer use the same setting.

Sanity check​

# Replace --profile ministack with exports if using Option A
aws --profile ministack sts get-caller-identity
aws --profile ministack cloudformation list-stacks \
--query "StackSummaries[?contains(StackName, \`${STACK_NAME}\`)].{Name:StackName,Status:StackStatus}" \
--output table

6. Jenkins CloudWatch log group​

CloudForge creates a log group for Fargate container output:

/aws/ecs/<stackName>/fargate/<securityProfile>

With securityProfile: DEV that is $LOG_GROUP → /aws/ecs/<stackName>/fargate/dev. The explicit name is set only when the security profile destroys logs on stack deletion (DEV); profiles that retain logs get a CDK-generated name, so look it up with describe-log-groups.

ECS uses the awslogs driver with stream prefix jenkins (applicationId). Jenkins prints first-run setup text and password hints to stdout, which lands in this log group on AWS and in MiniStack.

Confirm the log group exists:

aws --profile ministack logs describe-log-groups \
--log-group-name-prefix "/aws/ecs/${STACK_NAME}" \
--query 'logGroups[].logGroupName' \
--output table

List recent log streams (newest first):

aws --profile ministack logs describe-log-streams \
--log-group-name "$LOG_GROUP" \
--order-by LastEventTime \
--descending \
--max-items 10 \
--query 'logStreams[].[logStreamName,lastEventTimestamp]' \
--output table

Stream names typically look like: jenkins/<container-or-task-id>.


7. Read logs with AWS CLI​

Best when you do not know the stream name:

aws --profile ministack logs filter-log-events \
--log-group-name "$LOG_GROUP" \
--filter-pattern "?password ?Password ?admin ?Jenkins ?unlock" \
--limit 50 \
--query 'events[].[timestamp,message]' \
--output text

Narrower search:

aws --profile ministack logs filter-log-events \
--log-group-name "$LOG_GROUP" \
--filter-pattern "initialAdminPassword" \
--limit 20

Follow-style polling (run a few times after deploy):

aws --profile ministack logs filter-log-events \
--log-group-name "$LOG_GROUP" \
--start-time $(($(date +%s) * 1000 - 600000)) \
--filter-pattern "password" \
--output text

GetLogEvents — tail a specific stream​

After describe-log-streams, pick the newest jenkins/... stream:

STREAM=$(aws --profile ministack logs describe-log-streams \
--log-group-name "$LOG_GROUP" \
--order-by LastEventTime --descending --max-items 1 \
--query 'logStreams[0].logStreamName' --output text)

aws --profile ministack logs get-log-events \
--log-group-name "$LOG_GROUP" \
--log-stream-name "$STREAM" \
--start-from-head \
--limit 100 \
--query 'events[].message' \
--output text

Tail the last events:

aws --profile ministack logs get-log-events \
--log-group-name "$LOG_GROUP" \
--log-stream-name "$STREAM" \
--no-start-from-head \
--limit 50 \
--query 'events[].message' \
--output text

8. Initial admin password​

Jenkins writes the password to /var/jenkins_home/secrets/initialAdminPassword inside the container. On first boot it also prints guidance to the console (→ CloudWatch Logs).

Use FilterLogEvents or GetLogEvents above. Look for:

  • Jenkins initial setup is required
  • please use the following password
  • A 32-character hex string

Allow 1–3 minutes after ECS task start before logs appear.

Method 2 — Host bind mount file (local adaptation)​

The MiniStack adapter replaces EFS with a host directory. Read the file directly:

cd cfc-testing

# Path from stack output MiniStackHostVolumeJenkinsHome, or:
cat "${VOLUME_HOME}/secrets/initialAdminPassword"

If permission denied, fix ownership once (Jenkins runs as uid 1000):

sudo chown -R 1000:1000 "${VOLUME_HOME}"

Method 3 — Docker logs​

CONTAINER=$(docker ps --filter ancestor=jenkins/jenkins:lts --format '{{.Names}}' | head -1)
docker logs "$CONTAINER" 2>&1 | tail -80

Search for the password line:

docker logs "$CONTAINER" 2>&1 | grep -iE 'password|unlock|initial'

Method 4 — Exec into the container​

CONTAINER=$(docker ps --filter ancestor=jenkins/jenkins:lts --format '{{.Names}}' | head -1)
docker exec "$CONTAINER" cat /var/jenkins_home/secrets/initialAdminPassword

9. Open Jenkins​

# From stack output (port may differ — check MiniStackApplicationUrl)
open http://localhost:8080/

# Or follow ALB redirect
curl -sIL "$(aws --profile ministack cloudformation describe-stacks \
--stack-name "$MINISTACK_STACK" \
--query 'Stacks[0].Outputs[?OutputKey==`MiniStackLocalUrl`].OutputValue' \
--output text)"

Paste the initial admin password on the Unlock Jenkins page. Complete the setup wizard (install suggested plugins, create admin user). With the bind mount, wizard progress persists under .ministack-volumes/<stackName>/ across redeploys.


10. Useful AWS CLI queries (MiniStack)​

# Stack status and outputs
aws --profile ministack cloudformation describe-stacks \
--stack-name "$MINISTACK_STACK" \
--query 'Stacks[0].{Status:StackStatus,Outputs:Outputs}'

# Resources (Console → Stack → Resources)
aws --profile ministack cloudformation list-stack-resources \
--stack-name "$MINISTACK_STACK" \
--query 'StackResourceSummaries[].[LogicalResourceId,ResourceType,ResourceStatus]' \
--output table

# ECS cluster and service
aws --profile ministack ecs list-clusters
aws --profile ministack ecs list-services --cluster <cluster-arn>
aws --profile ministack ecs describe-services \
--cluster <cluster-arn> --services <service-arn>

# Recent stack failures
aws --profile ministack cloudformation describe-stack-events \
--stack-name "$MINISTACK_STACK" \
--query 'StackEvents[?contains(ResourceStatus,`FAILED`) || contains(ResourceStatus,`ROLLBACK`)].[Timestamp,ResourceStatus,LogicalResourceId,ResourceStatusReason]' \
--output table

11. Redeploy and clean up​

Update after config change — edit deployment-context.json, redeploy option 6.

Clean redeploy — delete the stack with MiniStackCli (there is no single Interactive Deployer option that both deletes and redeploys a MiniStack stack; option 3 is AWS-only), then choose option 6 again:

java -cp "target/classes:target/dependency/*" \
com.cloudforgeci.ministack.MiniStackCli \
delete "$MINISTACK_STACK"

Stop stale containers (frees port 8080):

docker ps --filter publish=8080
docker stop <container-name>

Remove persisted Jenkins home (fresh initial password):

rm -rf "cfc-testing/${VOLUME_HOME}"

Troubleshooting​

SymptomLikely causeFix
aws: bad CPU type or command not foundAWS CLI not installed for your Mac architectureInstall AWS CLI v2 or use Methods 2–4 for password
Empty filter-log-eventsTask still starting or logs not shipped yetWait 1–3 min; try docker logs
Log group not foundWrong STACK_NAME or stack not deployedVerify stackName in context; describe-log-groups --log-group-name-prefix /aws/ecs
8080 already allocatedOld ECS container still runningdocker stop container on 8080; redeploy
Rollback or preflight block on deployUnsupported resource types in the canonical templateCheck the preflight message and adaptation report; disable WAF/compliance features for MiniStack
Permission denied on bind mountHost dir not owned by uid 1000sudo chown -R 1000:1000 "${VOLUME_HOME}"

More: Troubleshooting


Next steps​