Amazon Linux 2 reaches EOL on June 30, 2026, so I tried letting that bird fly free

Amazon Linux 2 reaches EOL on June 30, 2026, so I tried letting that bird fly free

Amazon Linux 2 will reach EOL on June 30, 2026. Avoid continuing to use it and consider migrating to Amazon Linux 2023 or similar. That aside, this blog is releasing birds that are in Amazon Linux 2. If you want to release birds, please use this as a reference.
2026.06.30

This page has been translated by machine translation. View original

Good evening, this is Chiba (Ko).

The end-of-support date for Amazon Linux 2 has been set to June 30, 2026.

Looking back, it has a long history, having gone through the following events:

  • November 2017: Announcement of Amazon Linux 2
  • June 2018: Official release including 5-year LTS (Long Term Support)
  • Summer 2022: Support deadline extended from June 30, 2023 to June 30, 2024
  • December 2022: Support deadline extended from June 30, 2024 to June 30, 2025
  • December 2024: Support deadline extended from June 30, 2025 to June 30, 2026

I personally relied on it quite a bit around 2020.

When it comes to Amazon Linux 2, many people probably have the image of the following banner displayed when logging in via SSH:

       __|  __|_  )
       _|  (     /   Amazon Linux 2 AMI
      ___|\___|___|

AL2_banner

However, at some point, a bird started greeting you as shown below.

Amazon_Linux2_bird

   ,     #_
   ~\_  ####_        Amazon Linux 2
  ~~  \_#####\
  ~~     \###|       AL2 End of Life is 2025-06-30.
  ~~       \#/ ___
   ~~       V~' '->
    ~~~         /    A newer version of Amazon Linux is available!
      ~~._.   _/
         _/ _/       Amazon Linux 2023, GA and supported until 2028-03-15.
       _/m/'           https://aws.amazon.com/linux/amazon-linux-2023/

This is the familiar bird from the Amazon Linux 2023 login banner. When I see a bird of this shape, I want to dye it. Because I know how to dye it.

Just as having a drill makes you want to drill holes, knowing how to dye makes me want to dye this bird too. While I was thinking about what color would look good, I suddenly started thinking: wouldn't it be better to set it free rather than dye it?

To announce its EOL, this bird that has continued to stay in this place that is coming to an end — returning it to that sky. I began to feel that this was the role given to me now. When that thought struck me, lightning ran through the top of my head, and the colors of the world started to look different.

This time, I'd like to set this bird free. There is no longer any cage surrounding you. Let's convey that with everything we've got.

Summary First

  • Amazon Linux 2 will reach EOL on June 30, 2026, after which security updates will no longer be provided
  • Consider migrating to Amazon Linux 2023 or similar

https://docs.aws.amazon.com/ja_jp/elasticbeanstalk/latest/dg/using-features.migration-al.generic.from-al2.html

  • You won't have any trouble if you don't read the rest of the text below

Prologue: I Want to Set the Bird Free

The Day the Bird Came to Our Town

Since ancient times, humanity has lived viewing birds as some kind of symbolic presence. Sometimes as symbols of happiness, sometimes as spreaders of misfortune, and sometimes as heralds of seasonal change — and so on.

For us who have no means to soar through the sky, who have only been able to look up at them flying through the heavens on both wings from the ground, perhaps that is only natural. Even after we eventually became able to ride iron masses higher and faster than them through the sky, we continue to look up at birds. "Somewhere other than here." We continue to see them as Icons who can freely move, without any barriers, to that place we are always searching for.

Do you know when the bird came to Amazon Linux 2?

At some point, a bird started appearing in the Amazon Linux 2 login banner. I don't know the exact date, but well, roughly around September 20, 2023.

Excited that "a bird has come!!", I was launching instances from AMIs of that time and recording in my own notes whether a bird was there or not.

Quoted from notes at the time
% aws ec2 describe-images \
  --owners amazon \
  --filters "Name=name,Values=amzn2-ami-kernel-5.10*x86_64-gp2" \
  --query 'reverse(sort_by(Images, &CreationDate))[].[Name,ImageId]' \
  --output text
amzn2-ami-kernel-5.10-hvm-2.0.20230926.0-x86_64-gp2     ami-0adac58024a7f03bb bird present
amzn2-ami-kernel-5.10-hvm-2.0.20230912.0-x86_64-gp2     ami-03a1b4db103179555 bird absent
amzn2-ami-kernel-5.10-hvm-2.0.20230906.0-x86_64-gp2     ami-0f89bdd365c3d966d
amzn2-ami-kernel-5.10-hvm-2.0.20230822.0-x86_64-gp2     ami-07d6bd9a28134d3b3
amzn2-ami-kernel-5.10-hvm-2.0.20230808.0-x86_64-gp2     ami-044dbe71ee2d3c59e
amzn2-ami-kernel-5.10-hvm-2.0.20230727.0-x86_64-gp2     ami-0e25eba2025eea319 bird absent
amzn2-ami-kernel-5.10-hvm-2.0.20230719.0-x86_64-gp2     ami-09bad682e5ae72267
amzn2-ami-kernel-5.10-hvm-2.0.20230628.0-x86_64-gp2     ami-05ffd9ad4ddd0d6e2
amzn2-ami-kernel-5.10-hvm-2.0.20230612.0-x86_64-gp2     ami-0b51fe1c0254d8fc9
amzn2-ami-kernel-5.10-hvm-2.0.20230530.0-x86_64-gp2     ami-0d739893974bd27d0
amzn2-ami-kernel-5.10-hvm-2.0.20230515.0-x86_64-gp2     ami-07c2a88388bb80eb0
……

I can no longer access the AMIs from that time so I have no choice but to trust my own notes, but it says bird present for the 20230926 AMI and bird absent for the 20230912 AMI.

Of course, I must have included the following user data to prevent updates from being applied at launch from the AMI:

#cloud-config
repo_upgrade: none

So, that's roughly around when the bird came to Amazon Linux 2.

The Day I First Dyed a Bird

I normally work as a Solutions Architect at Classmethod, Inc. I started my current career in January 2020, and have been providing technical support to customers centered on AWS, doing pre-sales activities, writing blog posts, and giving presentations.

In parallel with that, I also started working as a Bird Dyer (torizome-shi) from August 2023. A Bird Dyer is someone who adds color to the bird in Amazon Linux 2023. My main activities consist of occasionally making the bird red and grinning thinking "it'd be interesting to dye this bird too." A unique hardship of being a Bird Dyer is that I tend to almost burst out laughing when I see the Amazon Linux bird during work.

The entry from when I first tried my hand at bird dyeing (torizome) is as follows:

https://dev.classmethod.jp/articles/i-dyed-amazon-linux-2023-bird-blue/

The approach taken was:

  • Change text color using color attribute escape sequences in the echo command
  • Place a bash script containing echo commands under /etc/update-motd.d/ to display the banner via motd at login time

Please use this as a reference if you want to try bird dyeing yourself.

I Want to Dye the Amazon Linux 2 Bird Too, But...

This is a tweet from September 22, 2023.

https://x.com/batchicchi/status/1705110141522305208

My past self from three years ago was trying to fulfill the role of Bird Dyer for the Amazon Linux 2 bird as well. I had actually even partly dyed it. However, midway through I noticed "oh, Amazon Linux 2023 and Amazon Linux 2 work a bit differently," started wondering "is this actually interesting...?", and came back to my senses thinking "what even is dyeing birds? Why am I doing this?", and ended up falling by the wayside. The last update time on my blog draft from that time had been frozen for about 2 years.

However, at this timing when Amazon Linux 2 is finally reaching EOL, I suddenly realized: birds aren't meant to be dyed, they're meant to be set free. Dyeing feels like a second helping, but setting free still feels fresh. Sounds fun. The bird would be happy. And now with generative AI it seems easy to set free.

And so I decided to fulfill my role as a Bird Freer (torinogashi). The Amazon Linux 2 bird, to just be a bird. There is no longer any cage surrounding you.

How to Set the Amazon Linux 2 Bird Free

I'll create a script that runs an animation of the bird flying away. Well, that should be easy enough to do quickly. I'd like to place that script in the right location and have it display when logging in via SSH.

Let me check on the motd-related things, just as I did when dyeing the bird in Amazon Linux 2023.

First, /etc/update-motd.d/:

$ ls -la /etc/update-motd.d/
total 24
drwxr-xr-x  2 root root   86 Jun  8  2021 .
drwxr-xr-x 81 root root 8192 Oct  4 15:18 ..
-rwxr-xr-x  1 root root  553 Sep  4 21:56 30-banner
-rwxr-xr-x  1 root root   67 Sep 13  2021 50-amazon-linux-extras-news
-rwxr-xr-x  1 root root  454 Sep  4 21:56 70-available-updates

The contents of the most important-looking /etc/update-motd.d/30-banner. Would replacing this be the quickest approach?

$ cat /etc/update-motd.d/30-banner 
#!/bin/sh
test -e /etc/os-release && os_release='/etc/os-release' || os_release='/usr/lib/os-release'
. "${os_release}"
cat << EOF
   ,     #_
   ~\_  ####_        ${PRETTY_NAME:-Amazon Linux 2}
  ~~  \_#####\\
  ~~     \###|       AL2 End of Life is ${SUPPORT_END:-2025-06-30}.
  ~~       \#/ ___
   ~~       V~' '->
    ~~~         /    A newer version of Amazon Linux is available!
      ~~._.   _/
         _/ _/       Amazon Linux 2023, GA and supported until 2028-03-15.
       _/m/'           https://aws.amazon.com/linux/amazon-linux-2023/

EOF

Let me also check the other files placed there.

$ cat /etc/update-motd.d/50-amazon-linux-extras-news 
#!/bin/bash
timeout 5s amazon-linux-extras system_motd 2>/dev/null
$ cat /etc/update-motd.d/70-available-updates 
#!/bin/bash

# Possible summaries include:
# No packages needed for security; %d packages available
# %d package(s) needed[ (+%d related)] for security, out of %d available
# There are [[%d security update(s)[ out of ]%d total update(s)]] available
LANG=C timeout 30s /usr/bin/yum \
    --debuglevel 2 \
    --security check-update 2>/dev/null \
        | grep -P '(?<! 0 packages) available$' \
    && echo 'Run "sudo yum update" to apply all updates.'

Let me also quickly check other paths. There are some things that exist in Amazon Linux 2023 but don't exist in Amazon Linux 2.

# ls -l /usr/lib/motd
ls: cannot access /usr/lib/motd: No such file or directory

# ls -l /etc/motd
lrwxrwxrwx 1 root root 25 Oct 13 22:21 /etc/motd -> /var/lib/update-motd/motd

# cat /var/lib/update-motd/motd 
   ,     #_
   ~\_  ####_        Amazon Linux 2
  ~~  \_#####\
  ~~     \###|       AL2 End of Life is 2025-06-30.
  ~~       \#/ ___
   ~~       V~' '->
    ~~~         /    A newer version of Amazon Linux is available!
      ~~._.   _/
         _/ _/       Amazon Linux 2023, GA and supported until 2028-03-15.
       _/m/'           https://aws.amazon.com/linux/amazon-linux-2023/

11 package(s) needed for security, out of 11 available
Run "sudo yum update" to apply all updates.

# ls -l /etc/motd.d/
ls: cannot access /etc/motd.d/: No such file or directory

# ls -l /run/motd
ls: cannot access /run/motd: No such file or directory

# ls -l /run/motd.d/
ls: cannot access /run/motd.d/: No such file or directory

Checking update-motd:

$ systemctl status update-motd
 update-motd.service - Dynamically Generate Message Of The Day
   Loaded: loaded (/usr/lib/systemd/system/update-motd.service; enabled; vendor preset: enabled)
   Active: active (exited) since Wed 2023-10-04 15:19:05 UTC; 1min 31s ago
  Process: 3126 ExecStart=/usr/sbin/update-motd (code=exited, status=0/SUCCESS)
 Main PID: 3126 (code=exited, status=0/SUCCESS)
   CGroup: /system.slice/update-motd.service

Oct 04 15:18:33 ip-172-31-38-67.ap-northeast-1.compute.internal systemd[1]: Starting Dynamically Generate Message Of The Day...
Oct 04 15:19:05 ip-172-31-38-67.ap-northeast-1.compute.internal systemd[1]: Started Dynamically Generate Message Of The Day.

Got a general idea.

Place the Script in /etc/profile.d/ Rather Than /etc/update-motd.d/

To state the conclusion first, placing the animation script under /etc/update-motd.d/ did not produce the expected behavior.

It seems that update-motd.service runs only once at boot, executes the scripts under /etc/update-motd.d/, writes the results to /var/lib/update-motd/motd, and subsequent SSH logins simply display that file. (Since spending too much time investigating here would make me start feeling "what am I even doing...", I cut my investigation short at roughly "it seems that way" level.)

To achieve animation, the script needs to run somewhere that can output directly to the TTY. This time I chose /etc/profile.d/.

Scripts in /etc/profile.d/ are sourced from /etc/profile when the SSH login shell starts. Since the TTY is available, interactive display is possible, such as moving the cursor with tput and adding colors.

Configuration for Setting the Amazon Linux 2 Bird Free

This time I'll set it free with the following configuration.

AL Bird diagram

I'll launch an Amazon Linux 2 EC2 instance in a public subnet and connect via SSH using EC2 Instance Connect from the AWS Management Console.

If the bird flies away when SSH connected, my role as a Bird Freer is complete.

By the way, where does the bird escape to? Does it go through the Internet Gateway and out to the internet?

AL Bird diagram path2

No, while this time I'm connecting via the internet to set it free, the bird should be able to escape even in a configuration that cannot do outbound to the internet. In that case, it would be like this.

Bird Fly Path

The bird escapes "upward." Birds aren't packets, so they can't read route tables. They probably won't go through the Internet Gateway. What am I saying.

Creating the Configuration to Set the Amazon Linux 2 Bird Free

I created a CloudFormation template that creates everything together.

Expand
free-al2-bird.yaml
AWSTemplateFormatVersion: "2010-09-09"
Description: Free the AL2 Bird - AL2 instance with bird animation on login

Parameters:
  InstanceType:
    Type: String
    Default: t3.micro

Resources:
  # VPC
  Vpc:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
      EnableDnsSupport: true
      EnableDnsHostnames: true
      Tags:
        - Key: Name
          Value: free-al2-bird-vpc

  InternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: free-al2-bird-igw

  VpcGatewayAttachment:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref Vpc
      InternetGatewayId: !Ref InternetGateway

  PublicSubnet:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref Vpc
      CidrBlock: 10.0.0.0/24
      AvailabilityZone: !Select [0, !GetAZs ""]
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: free-al2-bird-subnet-public

  RouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref Vpc
      Tags:
        - Key: Name
          Value: free-al2-bird-rtb-public

  DefaultRoute:
    Type: AWS::EC2::Route
    DependsOn: VpcGatewayAttachment
    Properties:
      RouteTableId: !Ref RouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway

  SubnetRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnet
      RouteTableId: !Ref RouteTable

  # Security Group
  SecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: free-al2-bird-sg
      GroupDescription: Allow SSH from EC2 Instance Connect (ap-northeast-1)
      VpcId: !Ref Vpc
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: 3.112.23.0/29
          Description: EC2 Instance Connect ap-northeast-1
      Tags:
        - Key: Name
          Value: free-al2-bird-sg

  # EC2
  Instance:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: ami-06087cfd700858bee
      InstanceType: !Ref InstanceType
      SubnetId: !Ref PublicSubnet
      SecurityGroupIds:
        - !Ref SecurityGroup
      Tags:
        - Key: Name
          Value: free-al2-bird-ec2
      UserData:
        Fn::Base64: |
          #!/bin/bash

          cat << 'BIRDSCRIPT' > /etc/profile.d/bird.sh
          #!/bin/bash

          # Run only at SSH login
          [ -t 1 ]                 || return 0
          [ -n "$TERM" ]           || return 0
          [ -n "$SSH_CONNECTION" ] || return 0

          Q="'"
          BOLD=$'\e[1m'
          RESET=$'\e[0m'
          RAINBOW=($'\e[31m' $'\e[33m' $'\e[32m' $'\e[36m' $'\e[34m' $'\e[35m')
          BIRD_H=10
          TRAVEL=60

          BANNER=(
              "   ,     #_"
              "   ~\\_  ####_        Amazon Linux 2"
              "  ~~  \\_#####\\"
              "  ~~     \\###|       AL2 End of Life is 2026-06-30."
              "  ~~       \\#/ ___"
              "   ~~       V~${Q} ${Q}->"
              "    ~~~         /    A newer version of Amazon Linux is available!"
              "      ~~._.   _/"
              "         _/ _/       Amazon Linux 2023, GA and supported until 2028-03-15."
              "       _/m/${Q}           https://aws.amazon.com/linux/amazon-linux-2023/"
          )

          BIRD=(
              "   ,     #_"
              "   ~\\_  ####_"
              "  ~~  \\_#####\\"
              "  ~~     \\###|"
              "  ~~       \\#/ ___"
              "   ~~       V~${Q} ${Q}->"
              "    ~~~         /"
              "      ~~._.   _/"
              "         _/ _/"
              "       _/m/${Q}"
          )

          draw_bird() {
              local col=$1 row_off=$2 i row
              shift 2
              for ((i = 0; i < BIRD_H; i++)); do
                  row=$(( BASE_ROW + row_off + i ))
                  if (( row >= 0 && row < ROWS )); then
                      tput cup "$row" 0
                      printf '%*s' "$COLS" ''
                      if (( col < COLS )); then
                          tput cup "$row" "$col"
                          if (( flying )); then
                              printf '%s' "${BOLD}${RAINBOW[$(( (i + col / 4) % 6 ))]}$1${RESET}"
                          else
                              printf '%s' "$1"
                          fi
                      fi
                  fi
                  shift
              done
          }

          clear_rows() {
              local row_off=$1 i row
              for ((i = 0; i < BIRD_H; i++)); do
                  row=$(( BASE_ROW + row_off + i ))
                  if (( row >= 0 && row < ROWS )); then
                      tput cup "$row" 0
                      printf '%*s' "$COLS" ''
                  fi
              done
          }

          draw_trail() {
              local bird_col=$1 bird_row_off=$2
              local bird_top=$(( BASE_ROW + bird_row_off ))
              local bird_bot=$(( bird_top + BIRD_H - 1 ))
              local bird_right=$(( bird_col + 22 ))
              local chars=('.' ',' '~' '*' '-')
              local i tr tc n j dots
              for ((i = 0; i < ${#trail_cols[@]}; i++)); do
                  tr=$(( trail_rows[i] + RANDOM % 3 - 1 ))
                  tc=$(( trail_cols[i] + RANDOM % 3 - 1 ))
                  (( tr < 0 || tc < 0 )) && continue
                  (( tr >= bird_top && tr <= bird_bot && tc >= bird_col && tc <= bird_right )) && continue
                  dots=''
                  n=$(( RANDOM % 2 + 1 ))
                  for ((j = 0; j < n; j++)); do dots+="${chars[$(( RANDOM % 5 ))]}"; done
                  tput cup "$tr" "$tc"
                  printf '%s' "$dots"
              done
          }

          COLS=$(tput cols)
          ROWS=$(tput lines)
          BASE_ROW=0

          tput civis
          trap 'tput cnorm; tput cup $(( ROWS - 1 )) 0' EXIT INT TERM
          clear

          flying=0
          draw_bird 0 0 "${BANNER[@]}"
          sleep 0.8

          flying=1
          col=0
          prev_row_off=0
          trail_cols=()
          trail_rows=()

          while (( col <= TRAVEL )); do
              row_off=$(( - col * BIRD_H / TRAVEL ))
              clear_rows "$prev_row_off"

              trail_cols+=("$(( col + 14 ))")
              trail_rows+=("$(( BASE_ROW + row_off + 5 ))")
              while (( ${#trail_cols[@]} > 6 )); do
                  trail_cols=("${trail_cols[@]:1}")
                  trail_rows=("${trail_rows[@]:1}")
              done

              draw_bird "$col" "$row_off" "${BIRD[@]}"
              draw_trail "$col" "$row_off"
              prev_row_off=$row_off
              sleep 0.08
              (( col += 4 ))
          done

          for ((i = 0; i < BIRD_H; i++)); do
              tput cup "$i" 0
              printf '%*s' "$COLS" ''
          done
          tput cup 0 0
          printf '%s\n' "Amazon Linux 2 EOL is 2026/06/30"
          LAST=$(last -n 2 -i "$USER" 2>/dev/null | grep -v "still logged" | grep "$USER" | head -1 | awk '{print $4,$5,$6,$7,"from",$3}')
          [[ "$LAST" =~ ^(Mon|Tue|Wed|Thu|Fri|Sat|Sun) ]] && printf 'Last login: %s\n' "$LAST"
          BIRDSCRIPT

          chmod +x /etc/profile.d/bird.sh

Outputs:
  InstanceId:
    Value: !Ref Instance
  PublicIp:
    Value: !GetAtt Instance.PublicIp

It creates a VPC and all related resources along with an EC2 instance, and also places the script via user data. Here is the list of resources created:

Logical ID Resource Type Name Tag
Vpc AWS::EC2::VPC free-al2-bird-vpc
InternetGateway AWS::EC2::InternetGateway free-al2-bird-igw
VpcGatewayAttachment AWS::EC2::VPCGatewayAttachment (no tag)
PublicSubnet AWS::EC2::Subnet free-al2-bird-subnet-public
RouteTable AWS::EC2::RouteTable free-al2-bird-rtb-public
DefaultRoute AWS::EC2::Route (no tag)
SubnetRouteTableAssociation AWS::EC2::SubnetRouteTableAssociation (no tag)
SecurityGroup AWS::EC2::SecurityGroup free-al2-bird-sg
Instance AWS::EC2::Instance free-al2-bird-ec2

The AMI ID used is hardcoded. From the following results, the most recent one is specified.

~ $ aws ec2 describe-images \
>   --owners amazon \
>   --filters "Name=name,Values=amzn2-ami-kernel-5.10*x86_64-gp2" \
>   --query 'reverse(sort_by(Images, &CreationDate))[].[Name,ImageId]' \
>   --output text
amzn2-ami-kernel-5.10-hvm-2.0.20260629.0-x86_64-gp2     ami-06087cfd700858bee
amzn2-ami-kernel-5.10-hvm-2.0.20260622.1-x86_64-gp2     ami-07c647553a2f27949
amzn2-ami-kernel-5.10-hvm-2.0.20260615.0-x86_64-gp2     ami-0187c7df27ec175e1
amzn2-ami-kernel-5.10-hvm-2.0.20260608.0-x86_64-gp2     ami-0464ea504c6b43640
amzn2-ami-kernel-5.10-hvm-2.0.20260526.0-x86_64-gp2     ami-0b268943ac284f0c8
amzn2-ami-kernel-5.10-hvm-2.0.20260515.0-x86_64-gp2     ami-0324af2ca26d828c5
amzn2-ami-kernel-5.10-hvm-2.0.20260511.1-x86_64-gp2     ami-0cc213517bf63b8ad
amzn2-ami-kernel-5.10-hvm-2.0.20260508.0-x86_64-gp2     ami-0e56fdd7002dd4962
amzn2-ami-kernel-5.10-hvm-2.0.20260504.0-x86_64-gp2     ami-04633509de692dab5
amzn2-ami-kernel-5.10-hvm-2.0.20260427.1-x86_64-gp2     ami-0f0cee67f8a8a3f07
amzn2-ami-kernel-5.10-hvm-2.0.20260413.0-x86_64-gp2     ami-0d8fbaea8b647edac
amzn2-ami-kernel-5.10-hvm-2.0.20260406.1-x86_64-gp2     ami-0b23654657ee2874f

The script placed in /etc/profile.d/ of the created EC2 instance is as follows:

bird.sh
#!/bin/bash

# Determine if sourced or directly executed and exit early
if (return 0 2>/dev/null); then
    _exit() { return "$1"; }
else
    _exit() { exit "$1"; }
fi
# Run only on SSH login
[ -t 1 ]                 || _exit 0
[ -n "$TERM" ]           || _exit 0
[ -n "$SSH_CONNECTION" ] || _exit 0

Q="'"
BOLD=$'\e[1m'
RESET=$'\e[0m'
RAINBOW=($'\e[31m' $'\e[33m' $'\e[32m' $'\e[36m' $'\e[34m' $'\e[35m')
BIRD_H=10
TRAVEL=60

BANNER=(
    "   ,     #_"
    "   ~\\_  ####_        Amazon Linux 2"
    "  ~~  \\_#####\\"
    "  ~~     \\###|       AL2 End of Life is 2026-06-30."
    "  ~~       \\#/ ___"
    "   ~~       V~${Q} ${Q}->"
    "    ~~~         /    A newer version of Amazon Linux is available!"
    "      ~~._.   _/"
    "         _/ _/       Amazon Linux 2023, GA and supported until 2028-03-15."
    "       _/m/${Q}           https://aws.amazon.com/linux/amazon-linux-2023/"
)

BIRD=(
    "   ,     #_"
    "   ~\\_  ####_"
    "  ~~  \\_#####\\"
    "  ~~     \\###|"
    "  ~~       \\#/ ___"
    "   ~~       V~${Q} ${Q}->"
    "    ~~~         /"
    "      ~~._.   _/"
    "         _/ _/"
    "       _/m/${Q}"
)

draw_bird() {
    local col=$1 row_off=$2 i row
    shift 2
    for ((i = 0; i < BIRD_H; i++)); do
        row=$(( BASE_ROW + row_off + i ))
        if (( row >= 0 && row < ROWS )); then
            tput cup "$row" 0
            printf '%*s' "$COLS" ''
            if (( col < COLS )); then
                tput cup "$row" "$col"
                if (( flying )); then
                    printf '%s' "${BOLD}${RAINBOW[$(( (i + col / 4) % 6 ))]}$1${RESET}"
                else
                    printf '%s' "$1"
                fi
            fi
        fi
        shift
    done
}

clear_rows() {
    local row_off=$1 i row
    for ((i = 0; i < BIRD_H; i++)); do
        row=$(( BASE_ROW + row_off + i ))
        if (( row >= 0 && row < ROWS )); then
            tput cup "$row" 0
            printf '%*s' "$COLS" ''
        fi
    done
}

draw_trail() {
    local bird_col=$1 bird_row_off=$2
    local bird_top=$(( BASE_ROW + bird_row_off ))
    local bird_bot=$(( bird_top + BIRD_H - 1 ))
    local bird_right=$(( bird_col + 22 ))
    local chars=('.' ',' '~' '*' '-')
    local i tr tc n j dots
    for ((i = 0; i < ${#trail_cols[@]}; i++)); do
        tr=$(( trail_rows[i] + RANDOM % 3 - 1 ))
        tc=$(( trail_cols[i] + RANDOM % 3 - 1 ))
        (( tr < 0 || tc < 0 )) && continue
        (( tr >= bird_top && tr <= bird_bot && tc >= bird_col && tc <= bird_right )) && continue
        dots=''
        n=$(( RANDOM % 2 + 1 ))
        for ((j = 0; j < n; j++)); do dots+="${chars[$(( RANDOM % 5 ))]}"; done
        tput cup "$tr" "$tc"
        printf '%s' "$dots"
    done
}

COLS=$(tput cols)
ROWS=$(tput lines)
BASE_ROW=0

tput civis
trap 'tput cnorm' EXIT INT TERM
clear

flying=0
draw_bird 0 0 "${BANNER[@]}"
sleep 0.8

flying=1
col=0
prev_row_off=0
trail_cols=()
trail_rows=()

while (( col <= TRAVEL )); do
    row_off=$(( - col * BIRD_H / TRAVEL ))
    clear_rows "$prev_row_off"

    trail_cols+=("$(( col + 14 ))")
    trail_rows+=("$(( BASE_ROW + row_off + 5 ))")
    while (( ${#trail_cols[@]} > 6 )); do
        trail_cols=("${trail_cols[@]:1}")
        trail_rows=("${trail_rows[@]:1}")
    done

    draw_bird "$col" "$row_off" "${BIRD[@]}"
    draw_trail "$col" "$row_off"
    prev_row_off=$row_off
    sleep 0.08
    (( col += 4 ))
done

# Clear all rows the bird may have passed through
for ((i = 0; i < BIRD_H; i++)); do
    tput cup "$i" 0
    printf '%*s' "$COLS" ''
done
tput cup 0 0
printf '%s\n' "Amazon Linux 2 will reach EOL on 2026/06/30"
LAST=$(last -n 2 -i "$USER" 2>/dev/null | grep -v "still logged" | grep "$USER" | head -1 | awk '{print $4,$5,$6,$7,"from",$3}')
[[ "$LAST" =~ ^(Mon|Tue|Wed|Thu|Fri|Sat|Sun) ]] && printf 'Last login: %s\n' "$LAST"

I deployed this CloudFormation template. Now, let's put it into practice!

I Let the Amazon Linux 2 Bird Fly Free

Let's try connecting to the deployed EC2 instance via EC2 Instance Connect.

ALBird1

Yes, the bird escaped! Did you catch it?

Let me show it one more time.

AL Bird2

Yes, it's flying away!! It might be hard to tell since it's a gif.

Locally the movement is a bit easier to see, so here it is.

ALbird iterm

There's a bird flying off to the upper right, changing colors in a rainbow of hues. I was able to return to the sky the bird that had been burdened with the role of announcing EOL. This is my debut as a bird releaser. The cage that surrounds you... no longer exists.

It comes back every time you connect, though. Sweat the small stuff and you lose.

Challenges

When I asked a generative AI "what is this?", it correctly identified it as a bird.

   ,     #_
   ~\_  ####_     
  ~~  \_#####\
  ~~     \###|
  ~~       \#/ ___   
   ~~       V~' '->
    ~~~         /
      ~~._.   _/
         _/ _/
       _/m/'

However, when I asked it to create about 3 alternative frames to make a flapping animation, it didn't go well. No matter how many times I asked for "a pattern with wings lowered," all it produced was a pile of symbols incomprehensible to the human brain. So I settled for a version where the bird flies away with a fixed pose.

I also struggled with dealing with the recurring feeling of "what on earth am I doing right now." I found myself apologizing to someone, saying that about 70% of this was written outside of work hours, so please forgive me. Perhaps those hours spent releasing a bird after the family had gone to sleep were, in some way, a source of comfort for me.

Closing

This was the story of how I let that bird fly free, since Amazon Linux 2 reaches EOL on June 30, 2026. To reiterate, Amazon Linux 2 reaches EOL on June 30, 2026. Security updates will no longer be provided. Consider migrating sooner rather than later.


IMG_3880


By the way, what is that bird's name anyway? I looked it up but couldn't find anything that clicked. The only example I found was in a GitHub issue where it was referred to as "AL bird." It might be nice to go along with that. If I were free to name it myself, I'd like to suggest the name "Rokunosuke."


IMG_3881


This blog was written for the purpose of raising awareness about Amazon Linux 2's EOL. I am absolutely not messing around. However, it may not be the most appropriate thing to read at work. When recommending it to others, please tell them to sneak a read during their break. I think the bird, from somewhere beyond the sky, would want that too.

That's all from Chiba Yuki (@batchicchi).


IMG_3883


         ,     #_
         ~\_  ####_     
        ~~  \_#####\
        ~~     \###|.      *flap*
        ~~       \#/ ___   
         ~~       V~' '->
          ~~~         /
            ~~._.   _/
               _/ _/
      (  ((   _/m/'

al bird last 2

References


クラスメソッドのエンジニアと1on1で話してみませんか?

選考に関係のないカジュアルな面談です。
「技術スタックや開発環境について詳しく知りたい」「実際のプロジェクト事例を聞きたい」「リモートワークや評価制度について確認したい」など、気になることを直接エンジニアに質問できます。
カジュアル面談に申し込む

Share this article

AWSのお困り事はクラスメソッドへ