Azure DevOps container agent to be idle and should not go exist

I want azure devops container agent always idle and should be running.
but it is only running as long build pipeline is running. after that it is existing. so in order to avoid that I added below command in the docker file in the end. but agent is gone after pipeiine run

CMD [“tail”, “-f”, “/dev/null”]

Dockerfile:

# Another option:
# FROM arm64v8/alpine
# ENV TARGETARCH="linux-musl-arm64"

RUN apk update && apk upgrade

# Installing OpenJDK 11, Node.js, and other required packages
RUN apk add --no-cache bash curl git icu-libs jq openjdk17-jre nodejs npm

ENV JAVA_HOME=/usr/lib/jvm/java-17-openjdk
ENV PATH="$JAVA_HOME/bin:$PATH"

# Verify Java and Node.js installations
RUN java -version && node -v && npm -v

WORKDIR /azp/

COPY ./start.sh ./
RUN chmod +x ./start.sh

RUN adduser -D agent
RUN chown agent ./
USER agent

# Another option is to run the agent as root.
# ENV AGENT_ALLOW_RUNASROOT="true"

ENTRYPOINT [ "./start.sh" ]
CMD ["tail", "-f", "/dev/null"]

Please suggest way to run container agent idle even when pipeline build completed.

You are aware that CMD is used as parameters for ENTRYPOINT?

I did not get you, could you please correct if something is missing?

Your container is running the following command:

./start.sh tail -f /dev/null

Not two separate commands

you should instead just edit start.sh, and add tail -f /dev/null at the end of it

By doing so, will container dont die so that container agent will not disappear right? after pipeline completed

Since tail -f /dev/null never ends, the main process for the container will technically not end, allowing the container to stay up and idle

I removed -f /dev/null and added in the start.sh file in the end.
but still respective container agent got deleted from agents section and you can see here below

The ^C I see there - Is that you manually stopping the container?

After pipeline succeeded, it is still hang there and it should come out of that. and then container agent should stay idle but that is not happening .

No…
Either you run the docker container attached to your terminal, in which case your terminal will display the container’s process.
since your process never ends with tail -f /dev/null, the container never ends, and your terminal stays attached to the container

If you run the container DETACHED, then your terminal will be free to run other tasks as your container stays up

Yes, now I ran in detached mode, I see container is created but it is not available in the agents section as before

shyam@DESKTOP-OO7EVK2:~/azp-agent-in-docker$ docker run -e AZP_URL="https://dev.azure.com/nextops123" -e AZP_TOKEN="XX" -e AZP_POOL="Default" -e AZP_AGENT_NAME="Cagent20" --name "azp-agent-linux-cgent20" -d newonetail
c38853a997f6978bfa75d92707477507c2beb46481456b849557529fb64662dc
shyam@DESKTOP-OO7EVK2:~/azp-agent-in-docker$

@deanayalon I see agent is available now in devops agent section, I noticed it is taking 5 min to come up.
Thank you. :slight_smile:
I did not figure out this earlier

and to add one more point i Just noticed that when I use below docker file, it is running even container exists.

shyam@DESKTOP-OO7EVK2:~/azp-agent-in-docker$ cat Dockerfile

FROM mcr.microsoft.com/dotnet/sdk:5.0

# Install curl
RUN apt-get update && apt-get install -y curl

# Copy your application code and script
COPY . /app

# Set the working directory
WORKDIR /app

# Ensure the script is executable
RUN chmod +x start.sh

# Use a shell form entrypoint to run the script and then the .NET application
# Run start.sh and then dotnet run; ensure dotnet run stays in the foreground
CMD ["/bin/sh", "-c", "./start.sh && dotnet run"]

and when I execute below docker file container is not getting killed.

FROM alpine:latest
ENV TARGETARCH="linux-musl-x64"

# Another option:
# FROM arm64v8/alpine
# ENV TARGETARCH="linux-musl-arm64"

RUN apk update && apk upgrade

# Installing OpenJDK 11, Node.js, and other required packages
RUN apk add --no-cache bash curl git icu-libs jq openjdk17-jre nodejs npm

ENV JAVA_HOME=/usr/lib/jvm/java-17-openjdk
ENV PATH="$JAVA_HOME/bin:$PATH"

# Verify Java and Node.js installations
RUN java -version && node -v && npm -v

WORKDIR /azp/

COPY ./start.sh ./
RUN chmod +x ./start.sh

RUN adduser -D agent
RUN chown agent ./
USER agent

# Another option is to run the agent as root.
# ENV AGENT_ALLOW_RUNASROOT="true"

ENTRYPOINT [ "./start.sh" ]

in order make sure container run with out existing i tried to add cmd and dotnet run test in the command section but first dockerfile above is making container to kill when i ran it

COuld you please suggest what exactly need to amend/add in dockerfile 1 in the above to makue container running with out exist

How about you share start.sh with us?

Here is the start.sh file, but first dockerfile only needs to be fixed , second one is making container running not existing

start.sh file:

#!/bin/bash
set -e

if [ -z "${AZP_URL}" ]; then
  echo 1>&2 "error: missing AZP_URL environment variable"
  exit 1
fi

if [ -z "${AZP_TOKEN_FILE}" ]; then
  if [ -z "${AZP_TOKEN}" ]; then
    echo 1>&2 "error: missing AZP_TOKEN environment variable"
    exit 1
  fi

  AZP_TOKEN_FILE="/azp/.token"
  echo -n "${AZP_TOKEN}" > "${AZP_TOKEN_FILE}"
fi

unset AZP_TOKEN

if [ -n "${AZP_WORK}" ]; then
  mkdir -p "${AZP_WORK}"
fi

cleanup() {
  trap "" EXIT

  if [ -e ./config.sh ]; then
    print_header "Cleanup. Removing Azure Pipelines agent..."

    # If the agent has some running jobs, the configuration removal process will fail.
    # So, give it some time to finish the job.
    while true; do
      ./config.sh remove --unattended --auth "PAT" --token $(cat "${AZP_TOKEN_FILE}") && break

      echo "Retrying in 30 seconds..."
      sleep 30
    done
  fi
}

print_header() {
  lightcyan="\033[1;36m"
  nocolor="\033[0m"
  echo -e "\n${lightcyan}$1${nocolor}\n"
}

# Let the agent ignore the token env variables
export VSO_AGENT_IGNORE="AZP_TOKEN,AZP_TOKEN_FILE"

print_header "1. Determining matching Azure Pipelines agent..."

AZP_AGENT_PACKAGES=$(curl -LsS \
    -u user:$(cat "${AZP_TOKEN_FILE}") \
    -H "Accept:application/json" \
    "${AZP_URL}/_apis/distributedtask/packages/agent?platform=${TARGETARCH}&top=1")

AZP_AGENT_PACKAGE_LATEST_URL=$(echo "${AZP_AGENT_PACKAGES}" | jq -r ".value[0].downloadUrl")

if [ -z "${AZP_AGENT_PACKAGE_LATEST_URL}" -o "${AZP_AGENT_PACKAGE_LATEST_URL}" == "null" ]; then
  echo 1>&2 "error: could not determine a matching Azure Pipelines agent"
  echo 1>&2 "check that account "${AZP_URL}" is correct and the token is valid for that account"
  exit 1
fi

print_header "2. Downloading and extracting Azure Pipelines agent..."

curl -LsS "${AZP_AGENT_PACKAGE_LATEST_URL}" | tar -xz & wait $!

source ./env.sh

trap "cleanup; exit 0" EXIT
trap "cleanup; exit 130" INT
trap "cleanup; exit 143" TERM

print_header "3. Configuring Azure Pipelines agent..."

./config.sh --unattended \
  --agent "${AZP_AGENT_NAME:-$(hostname)}" \
  --url "${AZP_URL}" \
  --auth "PAT" \
  --token $(cat "${AZP_TOKEN_FILE}") \
  --pool "${AZP_POOL:-Default}" \
  --work "${AZP_WORK:-_work}" \
  --replace \
  --acceptTeeEula & wait $!

I would try to add the tail -f /dev/null as a last line to the script.

Or use something like

print_header "4. Keeping container alive..."

# Infinite loop to keep the container running
while true; do
  sleep 1000
done

could you please confirm where exactly you will add? it is in first dock file only right?

In file start.sh.

Only ENTRYPOINT and no CMD in Dockerfile.

Entry point is already there in the docker file below

![image|614x500](upload://zfK2eDLEBEkA4DXboKPCzi7Gf5p.png)

and in existing start.sh file already there tail command below you can see

![image|690x432](upload://zWcyjZsBK65VY0tdGU4YK6JWEfM.png)

Could you please provide more info on what to do and where to add?

So you try to do what Microsoft documented here on your own?

Though, your start.sh looks incomplete compared to the one from the documentation.

And I love this note in the documentation:

You must also use a container orchestration system, like Kubernetes or Azure Container Instances, to start new copies of the container when the work completes.