<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: DaShaun</title>
    <description>The latest articles on DEV Community by DaShaun (@dashaun).</description>
    <link>https://dev.to/dashaun</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F51700%2F045fb338-fc52-4b39-8120-82993aa24647.jpg</url>
      <title>DEV Community: DaShaun</title>
      <link>https://dev.to/dashaun</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dashaun"/>
    <language>en</language>
    <item>
      <title>Spring AI + AGENTS.md Repository Steward</title>
      <dc:creator>DaShaun</dc:creator>
      <pubDate>Tue, 01 Sep 2026 21:56:22 +0000</pubDate>
      <link>https://dev.to/dashaun/spring-ai-agentsmd-repository-steward-2ol0</link>
      <guid>https://dev.to/dashaun/spring-ai-agentsmd-repository-steward-2ol0</guid>
      <description>&lt;p&gt;I have been having a lot of fun building &lt;a href="https://github.com/dashaun/spring-ai-agents-md" rel="noopener noreferrer"&gt;&lt;code&gt;spring-ai-agents-md&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let me back up.&lt;/p&gt;

&lt;p&gt;I had two motivations for creating this starter.&lt;/p&gt;

&lt;p&gt;First, I am an &lt;a href="https://aaif.io/community/ambassadors/" rel="noopener noreferrer"&gt;AAIF Ambassador&lt;/a&gt;. I needed to do some Ambassador'ing. I wanted to make a public contribution that tied directly to an AAIF project and helped developers understand it, try it, and build something useful with it. &lt;code&gt;AGENTS.md&lt;/code&gt; was the obvious choice for me.&lt;/p&gt;

&lt;p&gt;Second, I am building agents with Spring AI. I want to be able to build my own "Claude" or "Codex" with Spring AI, and I am getting closer all the time.&lt;/p&gt;

&lt;p&gt;I work with the popular coding agents. I am also building my own agents and agent harnesses. I do not want a completely different instruction format for each one. &lt;code&gt;AGENTS.md&lt;/code&gt; is a simple, open specification that works across those worlds. The same repository instructions can help the agents people already use and the agents I am creating with Spring AI.&lt;/p&gt;

&lt;p&gt;I really like that.&lt;/p&gt;

&lt;p&gt;The idea seemed simple enough.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://agents.md" rel="noopener noreferrer"&gt;&lt;code&gt;AGENTS.md&lt;/code&gt;&lt;/a&gt; gives coding agents a predictable place to find the instructions for a repository. &lt;a href="https://spring.io/projects/spring-ai" rel="noopener noreferrer"&gt;Spring AI&lt;/a&gt; gives Java developers a consistent way to build AI applications. I wanted to bring the two together.&lt;/p&gt;

&lt;p&gt;Put an &lt;code&gt;AGENTS.md&lt;/code&gt; file in a Spring project. Add a starter. Build a normal Spring AI &lt;code&gt;ChatClient&lt;/code&gt;. Let the agent get the right repository instructions automatically.&lt;/p&gt;

&lt;p&gt;Easy, right?&lt;/p&gt;

&lt;p&gt;Well, mostly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Phase 1: Make the simple thing simple
&lt;/h2&gt;

&lt;p&gt;The first goal was a Spring Boot starter that loads an &lt;code&gt;AGENTS.md&lt;/code&gt; document and adds it to Spring AI requests through a &lt;code&gt;CallAdvisor&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I did not want to invent a schema. An &lt;code&gt;AGENTS.md&lt;/code&gt; file is Markdown. People should be able to organize it in whatever way works for their project. The library preserves the complete document, including headings, lists, tables, code blocks, whitespace, and line endings.&lt;/p&gt;

&lt;p&gt;The starter handles the wiring. This is the dependency:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;io.github.spring-ai-community&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-ai-starter-agents-md&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;0.0.1-SNAPSHOT&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then you build the &lt;code&gt;ChatClient&lt;/code&gt; like you normally would.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Bean&lt;/span&gt;
&lt;span class="nc"&gt;ChatClient&lt;/span&gt; &lt;span class="nf"&gt;chatClient&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ChatClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Builder&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The auto-configuration attaches the advisor to the auto-configured builder. There is no special advisor registration required in the application.&lt;/p&gt;

&lt;p&gt;That part felt really good. It was the Spring experience I wanted.&lt;/p&gt;

&lt;h2&gt;
  
  
  Precedence was the hard part
&lt;/h2&gt;

&lt;p&gt;Loading one file was not the biggest struggle. Precedence was.&lt;/p&gt;

&lt;p&gt;What happens when a repository has more than one &lt;code&gt;AGENTS.md&lt;/code&gt; file?&lt;/p&gt;

&lt;p&gt;Which instructions apply to a file deep inside a project? Does the closest document replace the root document, or do the instructions accumulate? What happens when two documents disagree? What happens after a filesystem tool moves the agent into another part of the repository?&lt;/p&gt;

&lt;p&gt;I did not want the answers to be whatever felt right to me that day.&lt;/p&gt;

&lt;p&gt;Luckily, the proposed &lt;a href="https://github.com/agentsmd/agents.md/issues/135" rel="noopener noreferrer"&gt;&lt;code&gt;AGENTS.md&lt;/code&gt; v1.1 clarification&lt;/a&gt; arrived at the right time. It put names around the behavior I needed: jurisdiction, accumulation, precedence, and implicit inheritance.&lt;/p&gt;

&lt;p&gt;The rules are intuitive once they are written down:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An &lt;code&gt;AGENTS.md&lt;/code&gt; file applies to its directory and everything below it.&lt;/li&gt;
&lt;li&gt;Applicable guidance accumulates from ancestor directories.&lt;/li&gt;
&lt;li&gt;The closest instructions take precedence when guidance conflicts.&lt;/li&gt;
&lt;li&gt;Explicit user instructions override file-based instructions.&lt;/li&gt;
&lt;li&gt;Instructions from sibling directories do not apply.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The proposal is still a proposal, and the library says that clearly. But it gave me a solid target to implement and test instead of leaving the most important behavior implicit.&lt;/p&gt;

&lt;p&gt;For a target file, &lt;code&gt;spring-ai-agents-md&lt;/code&gt; walks toward the repository root, collects the applicable documents, and supplies them broadest first. Each complete document is kept intact inside a small framework-owned context envelope that explains the precedence.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;repository/AGENTS.md
        |
        v
repository/examples/AGENTS.md
        |
        v
repository/examples/steward/AGENTS.md
        |
        v
repository/examples/steward/src/RepositoryTools.java
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That final file gets the accumulated guidance, with the closest applicable instructions winning when there is a conflict.&lt;/p&gt;

&lt;p&gt;When an application already knows the target, it can be explicit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;chatClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;prompt&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;advisors&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;AgentsMdAdvisorParams&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"src/main/java/com/example/Example.java"&lt;/span&gt;&lt;span class="o"&gt;)))&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;call&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Filesystem tools can also propagate the path they accessed. The advisor resolves the instructions again on the next pass through the tool loop. That is the part that made the final example possible.&lt;/p&gt;

&lt;h2&gt;
  
  
  I wanted to be able to see what was happening
&lt;/h2&gt;

&lt;p&gt;I really wanted observability in this project.&lt;/p&gt;

&lt;p&gt;When an agent gives a surprising answer, I need to be able to debug it. Did the advisor find an &lt;code&gt;AGENTS.md&lt;/code&gt; file? Did it find one document or several? Did discovery stop at a safety limit? How much context did I add to the prompt?&lt;/p&gt;

&lt;p&gt;Without that feedback, every problem starts to look like a model problem.&lt;/p&gt;

&lt;p&gt;At the same time, an &lt;code&gt;AGENTS.md&lt;/code&gt; file can contain repository details that do not belong in logs, metric tags, or exception messages. I wanted useful visibility without copying the instructions into another system.&lt;/p&gt;

&lt;p&gt;The starter uses Spring Boot's normal logging configuration. At DEBUG, it reports the selected resource location and character count. It never writes the contents of &lt;code&gt;AGENTS.md&lt;/code&gt; to the logs.&lt;/p&gt;

&lt;p&gt;When an &lt;code&gt;ObservationRegistry&lt;/code&gt; is available, the advisor emits a focused Micrometer observation around the local prompt augmentation step:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;spring.ai.agents.md.advisor

document.state      = present | empty
document.count      = zero | one | multiple
resolution.outcome  = complete | depth-limit | document-limit | size-limit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The observation does not wrap the model call or duplicate the observations that Spring AI already provides. A separate &lt;code&gt;spring.ai.agents.md.context.size&lt;/code&gt; distribution summary records how many characters were added to the system prompt. The tags stay low-cardinality. Document contents and resource paths are never used as tags.&lt;/p&gt;

&lt;p&gt;I also wanted sane defaults. A repository should not be able to add an unlimited number of files, or an unlimited amount of text, to every model request.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Limit&lt;/th&gt;
&lt;th&gt;Default&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Directories inspected&lt;/td&gt;
&lt;td&gt;&lt;code&gt;32&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Documents composed&lt;/td&gt;
&lt;td&gt;&lt;code&gt;16&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Size of one document&lt;/td&gt;
&lt;td&gt;&lt;code&gt;64KB&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Total composed context&lt;/td&gt;
&lt;td&gt;&lt;code&gt;256KB&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Those limits are configurable. Invalid values fail during configuration instead of surprising you later. An oversized document is skipped whole, never silently truncated. When the total-size or document-count limit is reached, composition stops while keeping the accepted documents in broadest-to-closest order.&lt;/p&gt;

&lt;p&gt;Most importantly, the application gets feedback when that happens. The advisor publishes an &lt;code&gt;AgentsMdLimitReachedEvent&lt;/code&gt; with the normalized target, the limit that was reached, the number of documents accepted, the context size, and the configured limit. It does not contain the document contents.&lt;/p&gt;

&lt;p&gt;An interactive application can turn that event into immediate, useful feedback:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@EventListener&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;warnAboutAgentsMdLimit&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;AgentsMdLimitReachedEvent&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;err&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;printf&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="s"&gt;"AGENTS.md %s reached for %s; %d documents were applied.%n"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;outcome&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;acceptedDocumentCount&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That distinction matters to me. A safety limit should not quietly change what the model knows. If a limit changes the context, I want to know about it. I want enough information to fix the configuration or the repository without exposing the instructions I was trying to protect.&lt;/p&gt;

&lt;h2&gt;
  
  
  Phase 2: Prove it with a coding agent
&lt;/h2&gt;

&lt;p&gt;For Phase 2, I wanted something more convincing than a unit test and a chat endpoint.&lt;/p&gt;

&lt;p&gt;I built the &lt;a href="https://github.com/dashaun/spring-ai-agents-md/tree/main/examples/spring-ai-agents-md-coding-agent" rel="noopener noreferrer"&gt;&lt;code&gt;Repository Steward&lt;/code&gt;&lt;/a&gt;, an interactive coding agent built with Spring AI and Spring Shell 4.0.3.&lt;/p&gt;

&lt;p&gt;The steward can list, search, and read files inside a bounded repository workspace. As it moves through the repository, its filesystem tools propagate the active path. The &lt;code&gt;AgentsMdSystemAdvisor&lt;/code&gt; refreshes the applicable instructions for that path.&lt;/p&gt;

&lt;p&gt;The architecture looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Spring Shell `steward` command
            |
            v
Spring AI ChatClient + tool loop
            |
            +--&amp;gt; AgentsMdSystemAdvisor
            |       refreshes instructions for the active path
            |
            +--&amp;gt; listFiles / searchFiles / readFile
            |
            +--&amp;gt; proposePatch --&amp;gt; pending ChangeProposal
                                      |
                         show-change / apply-change
                                      |
                                      v
                              atomic filesystem write
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The model can explore the repository and propose a patch. It cannot approve its own change. &lt;code&gt;apply-change&lt;/code&gt; is a Spring Shell command, not a model tool. A person has to review the proposal and run the command.&lt;/p&gt;

&lt;p&gt;The safety boundaries are implemented in Java, not trusted to the prompt.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Paths must remain inside the configured workspace.&lt;/li&gt;
&lt;li&gt;Absolute paths and &lt;code&gt;..&lt;/code&gt; escapes are rejected.&lt;/li&gt;
&lt;li&gt;Symbolic links and protected directories such as &lt;code&gt;.git&lt;/code&gt; and &lt;code&gt;target&lt;/code&gt; are rejected.&lt;/li&gt;
&lt;li&gt;Reads, searches, file sizes, depth, and result counts are bounded.&lt;/li&gt;
&lt;li&gt;A patch must replace one exact, unique piece of text.&lt;/li&gt;
&lt;li&gt;Each proposal stores a digest of the file that was reviewed.&lt;/li&gt;
&lt;li&gt;The digest is checked again immediately before the atomic write.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the file changed after the proposal was reviewed, the proposal is stale and the write is refused.&lt;/p&gt;

&lt;p&gt;That is one of my favorite parts of the demo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Was the final example necessary?
&lt;/h2&gt;

&lt;p&gt;Yes.&lt;/p&gt;

&lt;p&gt;Was it too much?&lt;/p&gt;

&lt;p&gt;Maybe.&lt;/p&gt;

&lt;p&gt;The Repository Steward turned a focused Spring Boot starter into a real CLI coding agent, with filesystem containment, proposal storage, diffs, atomic writes, stale-change protection, Spring Shell commands, JLine, and a lot more tests.&lt;/p&gt;

&lt;p&gt;It also forced the model to do something that sounds simple but is not. The model has to inspect a file, preserve exact text across multiple calls, invoke a structured tool with the right arguments, process the result, and return to the conversation.&lt;/p&gt;

&lt;p&gt;I tried a lot of models.&lt;/p&gt;

&lt;p&gt;Some stopped after reading the file. Some printed JSON that looked like a tool call instead of calling the tool. Some invented a proposal ID. One changed the registered &lt;code&gt;readFile&lt;/code&gt; tool name to &lt;code&gt;read&lt;/code&gt;, which Spring AI correctly rejected. A model advertising tool support does not mean it is good at multi-step structured tool calling.&lt;/p&gt;

&lt;p&gt;At first, I wondered if my laptop was the problem. So I ran the same workflow against larger hardware and more models. The hardware helped with speed. It did not turn every model into a reliable coding agent.&lt;/p&gt;

&lt;p&gt;That was humbling, but useful.&lt;/p&gt;

&lt;p&gt;The example might be more than the starter needed. It is also the best proof that the starter works. It shows AGENTS.md instructions changing as an agent moves through a repository. It shows Spring AI driving a recursive tool loop. It shows the difference between instructions for a model and safety boundaries enforced by an application.&lt;/p&gt;

&lt;p&gt;It also made one requirement very clear: use a model that is good at multi-step structured tool calling.&lt;/p&gt;

&lt;h2&gt;
  
  
  Watch the Repository Steward
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://youtu.be/bIlYmp_iiBM" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2ivs8u68c1qk7tb2l6b5.jpg" alt="Repository Steward demo" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The video is less than one minute. It shows the steward:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Confirming its bounded workspace.&lt;/li&gt;
&lt;li&gt;Discovering the &lt;code&gt;AGENTS.md&lt;/code&gt; files that apply to a nested Java file.&lt;/li&gt;
&lt;li&gt;Inspecting the implementation before answering a question.&lt;/li&gt;
&lt;li&gt;Creating a pending patch without changing the file.&lt;/li&gt;
&lt;li&gt;Showing the proposed diff.&lt;/li&gt;
&lt;li&gt;Applying it only after explicit approval.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That is the story I wanted the example to tell.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I think this matters
&lt;/h2&gt;

&lt;p&gt;I am building my own agents and agent harnesses with Spring AI. I am not sure how many other people are doing the same thing yet.&lt;/p&gt;

&lt;p&gt;I do know that I want the instructions for those agents to live with the code. I want the format to be open. I want nested projects to provide focused context without copying everything from the repository root. I want the behavior to be easy to explain. I want the Spring integration to feel like Spring.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;spring-ai-agents-md&lt;/code&gt; is small, but I think it is really valuable.&lt;/p&gt;

&lt;p&gt;This has been really fun. I am really excited about this project. The tests are green, the repository is public, the example is documented, and the demo is recorded.&lt;/p&gt;

&lt;p&gt;Now that the blog is done, let's see if I can get this pulled into &lt;a href="https://github.com/spring-ai-community" rel="noopener noreferrer"&gt;&lt;code&gt;spring-ai-community&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I have put a lot of work into it. I am ready for feedback, but I am still nervous.&lt;/p&gt;

&lt;p&gt;That probably means it is time to share it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/dashaun/spring-ai-agents-md" rel="noopener noreferrer"&gt;&lt;code&gt;spring-ai-agents-md&lt;/code&gt; repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/dashaun/spring-ai-agents-md/tree/main/examples/spring-ai-agents-md-coding-agent" rel="noopener noreferrer"&gt;Repository Steward example&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://youtu.be/bIlYmp_iiBM" rel="noopener noreferrer"&gt;Repository Steward video&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://agents.md" rel="noopener noreferrer"&gt;&lt;code&gt;AGENTS.md&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/agentsmd/agents.md/issues/135" rel="noopener noreferrer"&gt;&lt;code&gt;AGENTS.md&lt;/code&gt; v1.1 clarification proposal&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aaif.io/" rel="noopener noreferrer"&gt;Agentic AI Foundation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://spring.io/projects/spring-ai" rel="noopener noreferrer"&gt;Spring AI&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>springai</category>
      <category>java</category>
      <category>ai</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Multi-Architecture Spring OCI from anywhere with Paketo</title>
      <dc:creator>DaShaun</dc:creator>
      <pubDate>Tue, 17 Dec 2024 17:33:00 +0000</pubDate>
      <link>https://dev.to/dashaun/multi-architecture-spring-oci-from-anywhere-with-paketo-1ic2</link>
      <guid>https://dev.to/dashaun/multi-architecture-spring-oci-from-anywhere-with-paketo-1ic2</guid>
      <description>&lt;h2&gt;
  
  
  Multi-Architecture Spring OCI from anywhere with Paketo
&lt;/h2&gt;

&lt;p&gt;My favorite feature of Spring Boot 3.4.0 is the adoption of multi-architecture buildpacks.&lt;br&gt;
This feature empowers you to construct native images and traditional JVM-based applications for both AMD64 and ARM64, from a unified build process.&lt;br&gt;
The fact that OCI images will also be smaller because of the new default buildpack is a bonus.&lt;/p&gt;
&lt;h3&gt;
  
  
  Emotionally Invested
&lt;/h3&gt;

&lt;p&gt;I've been building native images for ARM64 since 2021, because I'm a Raspberry Pi enthusiast and passionate about Spring Boot.&lt;br&gt;
Since that time, I've created dozens of solutions, to put production-ready, enterprise-grade, Spring Boot on Raspberry Pi ARM64 devices with only 512MB of RAM.&lt;br&gt;
I no longer have to support my own solution.&lt;/p&gt;
&lt;h3&gt;
  
  
  Getting Started
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Prerequisites:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;At least Java 17&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Docker&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Docker Desktop Configuration
&lt;/h3&gt;

&lt;p&gt;Docker Desktop comes with built-in QEMU support for multi-architecture builds. However, if you need to install QEMU manually, you have two options:&lt;/p&gt;
&lt;h4&gt;
  
  
  Option 1: Using tonistiigi/binfmt
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install QEMU support for all architectures&lt;/span&gt;
docker run &lt;span class="nt"&gt;--privileged&lt;/span&gt; &lt;span class="nt"&gt;--rm&lt;/span&gt; tonistiigi/binfmt &lt;span class="nt"&gt;--install&lt;/span&gt; all
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;This command was found here: &lt;a href="https://docs.docker.com/build/building/multi-platform/" rel="noopener noreferrer"&gt;https://docs.docker.com/build/building/multi-platform/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;
  
  
  Option 2: Using multiarch/qemu-user-static
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install and configure QEMU&lt;/span&gt;
docker run &lt;span class="nt"&gt;--privileged&lt;/span&gt; &lt;span class="nt"&gt;--rm&lt;/span&gt; multiarch/qemu-user-static &lt;span class="nt"&gt;--reset&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="nb"&gt;yes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;More information can be found here: &lt;a href="https://github.com/multiarch/qemu-user-static" rel="noopener noreferrer"&gt;https://github.com/multiarch/qemu-user-static&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  Spring Boot 3.4.0 application
&lt;/h3&gt;

&lt;p&gt;First, let's create a new Spring Boot application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;mydemo
&lt;span class="nb"&gt;cd &lt;/span&gt;mydemo
curl https://start.spring.io/starter.tgz &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nv"&gt;dependencies&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;web,actuator &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;maven-project | &lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-xvzf&lt;/span&gt; -
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;You could also use &lt;a href="https://start.spring.io/#!type=maven-project&amp;amp;language=java&amp;amp;platformVersion=3.4.0&amp;amp;packaging=jar&amp;amp;jvmVersion=23&amp;amp;groupId=com.example&amp;amp;artifactId=demo&amp;amp;name=demo&amp;amp;description=Demo%20project%20for%20Spring%20Boot&amp;amp;packageName=com.example.demo&amp;amp;dependencies=web,actuator" rel="noopener noreferrer"&gt;https://start.spring.io&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Create a multi-architecture OCI Image
&lt;/h3&gt;

&lt;p&gt;Here's the step-by-step process to build and publish multi-architecture images:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Build ARM64 image&lt;/span&gt;
./mvnw spring-boot:build-image &lt;span class="nt"&gt;-Dspring-boot&lt;/span&gt;.build-image.imagePlatform&lt;span class="o"&gt;=&lt;/span&gt;linux/arm64 &lt;span class="nt"&gt;-Dspring-boot&lt;/span&gt;.build-image.imageName&lt;span class="o"&gt;=&lt;/span&gt;dashaun/blog-3-4-0:arm64
docker push dashaun/blog-3-4-0:arm64

&lt;span class="c"&gt;# Build AMD64 image&lt;/span&gt;
./mvnw spring-boot:build-image &lt;span class="nt"&gt;-Dspring-boot&lt;/span&gt;.build-image.imagePlatform&lt;span class="o"&gt;=&lt;/span&gt;linux/amd64 &lt;span class="nt"&gt;-Dspring-boot&lt;/span&gt;.build-image.imageName&lt;span class="o"&gt;=&lt;/span&gt;dashaun/blog-3-4-0:amd64
docker push dashaun/blog-3-4-0:amd64

&lt;span class="c"&gt;# Create and push multi-architecture manifest&lt;/span&gt;
docker manifest create dashaun/blog-3-4-0:multiarch &lt;span class="nt"&gt;--amend&lt;/span&gt; dashaun/blog-3-4-0:arm64 &lt;span class="nt"&gt;--amend&lt;/span&gt; dashaun/blog-3-4-0:amd64
docker manifest push dashaun/blog-3-4-0:multiarch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Inspect
&lt;/h3&gt;

&lt;p&gt;Inspect the multi-architecture manifest:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker manifest inspect dashaun/blog-3-4-0:multiarch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Inspect the manifest, results below.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"schemaVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mediaType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"application/vnd.docker.distribution.manifest.list.v2+json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"manifests"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"mediaType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"application/vnd.docker.distribution.manifest.v2+json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"size"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2407&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"digest"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sha256:96d95eed9308f86a8055156913ed8710c25917bdf0389bd6d5cf2ab0a79c77fc"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"platform"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"architecture"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"amd64"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"os"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"linux"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"mediaType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"application/vnd.docker.distribution.manifest.v2+json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"size"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2406&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"digest"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sha256:4c42eebe412f56a03d840d2660b9defde3820ce1c5e510ffba0067be0ae11c8e"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"platform"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"architecture"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arm64"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"os"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"linux"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;The manifest shows both platforms &lt;code&gt;linux/amd64&lt;/code&gt; and &lt;code&gt;linux\arm64&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Looking at the &lt;a href="https://hub.docker.com/r/dashaun/blog-3-4-0/tags" rel="noopener noreferrer"&gt;Docker Hub tags&lt;/a&gt; &lt;code&gt;Digest&lt;/code&gt; sections, notice that the &lt;code&gt;sha&lt;/code&gt; values for the &lt;code&gt;arm64&lt;/code&gt; and &lt;code&gt;amd64&lt;/code&gt; tags match the &lt;code&gt;multiarch&lt;/code&gt; tag's values.&lt;/p&gt;

&lt;p&gt;The new default buildpack, &lt;a href="https://github.com/paketo-buildpacks/builder-jammy-java-tiny" rel="noopener noreferrer"&gt;builder-jammy-java-tiny&lt;/a&gt; also results in much smaller images.  Both of the images we created here are under 275mb.&lt;br&gt;
Switching to Spring Boot 3.3.6 and the previous default buildpack &lt;a href="https://github.com/paketo-buildpacks/builder-jammy-base" rel="noopener noreferrer"&gt;paketobuildpacks/builder-jammy-base&lt;/a&gt; results in a ~350mb image.&lt;/p&gt;
&lt;h3&gt;
  
  
  Building Native Images
&lt;/h3&gt;

&lt;p&gt;The process for building native images is similar, with the addition of GraalVM support:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;my-native-demo
&lt;span class="nb"&gt;cd &lt;/span&gt;my-native-demo
curl https://start.spring.io/starter.tgz &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nv"&gt;dependencies&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;web,actuator,native &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;maven-project | &lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-xvzf&lt;/span&gt; -
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;You could also use &lt;a href="https://start.spring.io/#!type=maven-project&amp;amp;language=java&amp;amp;platformVersion=3.4.0&amp;amp;packaging=jar&amp;amp;jvmVersion=23&amp;amp;groupId=com.example&amp;amp;artifactId=demo&amp;amp;name=demo&amp;amp;description=Demo%20project%20for%20Spring%20Boot&amp;amp;packageName=com.example.demo&amp;amp;dependencies=web,actuator,native" rel="noopener noreferrer"&gt;https://start.spring.io&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Building Multi-Architecture Native Images
&lt;/h3&gt;

&lt;p&gt;When the buildpack see's the &lt;code&gt;native-maven-plugin&lt;/code&gt; in the project, it creates native images with GraalVM. Our steps won't change, we will just use different tags.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Build ARM64 native image&lt;/span&gt;
./mvnw spring-boot:build-image &lt;span class="nt"&gt;-Dspring-boot&lt;/span&gt;.build-image.imagePlatform&lt;span class="o"&gt;=&lt;/span&gt;linux/arm64 &lt;span class="nt"&gt;-Dspring-boot&lt;/span&gt;.build-image.imageName&lt;span class="o"&gt;=&lt;/span&gt;dashaun/blog-3-4-0:native-arm64
docker push dashaun/blog-3-4-0:native-arm64

&lt;span class="c"&gt;#Build AMD64 native image&lt;/span&gt;
./mvnw spring-boot:build-image &lt;span class="nt"&gt;-Dspring-boot&lt;/span&gt;.build-image.imagePlatform&lt;span class="o"&gt;=&lt;/span&gt;linux/amd64 &lt;span class="nt"&gt;-Dspring-boot&lt;/span&gt;.build-image.imageName&lt;span class="o"&gt;=&lt;/span&gt;dashaun/blog-3-4-0:native-amd64
docker push dashaun/blog-3-4-0:native-amd64

&lt;span class="c"&gt;# Create and push multi-architecture manifest&lt;/span&gt;
docker manifest create dashaun/blog-3-4-0:native-multiarch &lt;span class="nt"&gt;--amend&lt;/span&gt; dashaun/blog-3-4-0:native-arm64 &lt;span class="nt"&gt;--amend&lt;/span&gt; dashaun/blog-3-4-0:native-amd64
docker manifest push dashaun/blog-3-4-0:native-multiarch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Inspect Native
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker manifest inspect dashaun/blog-3-4-0:native-multiarch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Inspect the manifest, results below.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"schemaVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"mediaType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"application/vnd.docker.distribution.manifest.list.v2+json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"manifests"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"mediaType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"application/vnd.docker.distribution.manifest.v2+json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"size"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2407&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"digest"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sha256:70311015a28ff9ebbaa0ef0552e91bd85d2220716b69a8444ecf18ae542fa3f1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"platform"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"architecture"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"amd64"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"os"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"linux"&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"mediaType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"application/vnd.docker.distribution.manifest.v2+json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"size"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2406&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"digest"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sha256:4b9243d946a61f5fd8c9762f9e8a5ef107ba5a51a3679faa54b5a3cd96e4147c"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"platform"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"architecture"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arm64"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"os"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"linux"&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;The manifest shows both platforms &lt;code&gt;linux/amd64&lt;/code&gt; and &lt;code&gt;linux/arm64&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Take a look at these image sizes!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dashaun/blog-3-3-6                         latest           0e61fed5cf4a   44 years ago   348MB
dashaun/blog-3-4-0                         amd64            6ce61c588eba   44 years ago   268MB
dashaun/blog-3-4-0                         native-amd64     9f63e381e26c   44 years ago   117MB
dashaun/blog-3-4-0                         native-arm64     7a9b08bf964a   44 years ago   111MB
dashaun/blog-3-4-0                         arm64            0d07d6f15c86   44 years ago   259MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I did this experiment on MacOS with ARM64 (M3 chip), but these exact same steps can be used on either ARM64 or AMD64 machines.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Spring Boot 3.4.0's multi-architecture support with Paketo buildpacks revolutionizes how we build and deploy applications across different architectures. Whether you're targeting cloud platforms or edge devices like Raspberry Pi, you can now build and deploy with confidence.&lt;/p&gt;

&lt;p&gt;The combination of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Multi-architecture support&lt;/li&gt;
&lt;li&gt;Smaller image sizes&lt;/li&gt;
&lt;li&gt;Native image capabilities&lt;/li&gt;
&lt;li&gt;Simplified build process&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Makes Spring Boot 3.4.0 a compelling choice for modern cloud-native and edge-native applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep Learning
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-3.4-Release-Notes" rel="noopener noreferrer"&gt;Spring Boot 3.4 Release Notes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/live/fdyj_b-Mj4c?si=MA9azUhPg1tw09Kv" rel="noopener noreferrer"&gt;YouTube with Karanbir Singh&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://neuw.medium.com/multi-architecture-oci-images-spring-boot-3-4-x-d8f2a0f7d6e6" rel="noopener noreferrer"&gt;Karanbir Singh's Blog&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/paketo-buildpacks/builder-jammy-java-tiny" rel="noopener noreferrer"&gt;paketobuildpacks/builder-jammy-java-tiny&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>springboot</category>
      <category>paketo</category>
      <category>buildpacks</category>
      <category>multiarch</category>
    </item>
    <item>
      <title>Spring Health Assessment Report</title>
      <dc:creator>DaShaun</dc:creator>
      <pubDate>Sat, 17 Feb 2024 03:11:50 +0000</pubDate>
      <link>https://dev.to/dashaun/spring-health-assessment-report-14oj</link>
      <guid>https://dev.to/dashaun/spring-health-assessment-report-14oj</guid>
      <description>&lt;p&gt;Spring Boot 3.0.0 was released 450 days ago. Helping people upgrade, and enjoy all the amazing benefits, has been one of my passions for about 450 days.  I'm a Spring Developer Advocate and I love my job.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://twitter.com/tschuehly/status/1723106405756846117" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdashaun.com%2Fposts%2Fspring-health-assessment-report%2Fimages%2Fnormalconversation.png" alt="Thomas Schuehly has thoughts"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It's a passion really. Thomas Schuehly gets it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;At SpringOne 2023 in Las Vegas, my friend, Joachim Pasquali, over at Fiserv, shared a story that I'll not soon forget.  The process of upgrading and patching, doesn't have to be a painful process.  On that day, his flagship product, was on the latest and greatest version of Spring Boot.  &lt;a href="https://youtube.com/clip/UgkxKNJxAWtsIWDzsFJM8tDB8VvG1E66EMVk?si=0e7-mUIHfjIHl9Qp" rel="noopener noreferrer"&gt;Watch the clip that gave me all the feels here.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I want you to know about some tools and techniques that can help you upgrade your Spring Boot projects with more joy, frequently, and safely.&lt;/p&gt;

&lt;p&gt;First things first, let's talk about the challenges. Outdated Spring Boot versions can expose your projects to security risks, leave you without access to the latest features and performance improvements, and can be costing you more money. Have you ever been working, on an outdated JVM perhaps, or an older Spring Boot version, and secretly wished you were using the latest release? I get it, I've been there too, and I'm here to help.&lt;/p&gt;

&lt;p&gt;I want you to check out the &lt;a href="https://tanzu.vmware.com/spring-health-assessment" rel="noopener noreferrer"&gt;Spring Health Assessment Report&lt;/a&gt;.  This tool is very similar to something you probably have running already, it tells you about known vulnerabilities. However, this tool is also unique in a few very special ways.  First of all, it's free for you to use.  It also has the support timeline information built in. It provides a measure of how hard it will be to upgrade. Finally, it only reports on the Spring and Micrometer dependencies that you are using. It's very focused and actionable.&lt;/p&gt;

&lt;p&gt;I like to call &lt;a href="https://docs.openrewrite.org" rel="noopener noreferrer"&gt;OpenRewrite&lt;/a&gt; my favorite &lt;code&gt;cheat code&lt;/code&gt;. When I say that I demonstrate &lt;code&gt;OpenRewrite&lt;/code&gt; twenty times per week, I'm not exaggerating.  It's a powerful tool, and I'm still surprised by how many developers still don't know about it.  &lt;a href="https://github.com/dashaun/openrewrite-spring-boot-upgrade-example" rel="noopener noreferrer"&gt;This repository includes one of those demos, that I deliver multiple times every week&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Software is soft for a reason.  It's meant to be changed.  It's meant to be upgraded.  It's meant to be improved.  Java comes out with a new JVM, every 6-months.  The Spring team releases a new Spring Boot version every 6-months.  It's time to start upgrading more frequently.  In 2023, there were only 4-weeks, of the entire year, that the Spring team didn't release something new.  You can see for yourself over at &lt;a href="https://calendar.spring.io" rel="noopener noreferrer"&gt;calendar-dot-spring-dot-io&lt;/a&gt;.  It's time to start upgrading more frequently.&lt;/p&gt;

&lt;p&gt;I believe that the way software is going to be measured, going forward, is by how easy it is to maintain. I'm convinced that continuous upgrading, and continuous patching, is going to be a key differentiator for software projects.  I want you to get started, today.&lt;/p&gt;

&lt;p&gt;By upgrading your Spring Boot projects, you can benefit from improved security, performance, and access to the latest features. You'll also be able to take advantage of the ongoing support and updates provided by the Spring team and the Java team!&lt;/p&gt;

&lt;p&gt;So why wait? &lt;a href="https://youtu.be/XI_7mpEiPjI" rel="noopener noreferrer"&gt;Watch this video I made last week&lt;/a&gt;. Check out the Spring Health Assessment Report. You will see a few reasons why you should upgrade.  You will also see how easy it can be. Spring Boot 3.2.3 comes out next Thursday, how long will it take you to upgrade?&lt;/p&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://youtu.be/XI_7mpEiPjI" rel="noopener noreferrer"&gt;Spring into Action: Boost Your Java Projects with Essential Upgrade Tools!&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;&lt;a href="https://tanzu.vmware.com/spring-health-assessment" rel="noopener noreferrer"&gt;Spring Health Assessment Report&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/tschuehly/status/1723106405756846117" rel="noopener noreferrer"&gt;Thomas Schuehly gets it&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/dashaun/openrewrite-spring-boot-upgrade-example" rel="noopener noreferrer"&gt;OpenRewrite Spring Boot Upgrade Example&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://youtube.com/clip/UgkxKNJxAWtsIWDzsFJM8tDB8VvG1E66EMVk?si=bF6GiJRZgWbH78JX" rel="noopener noreferrer"&gt;Joachim Pasquali at SpringOne 2023&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>springboot</category>
    </item>
    <item>
      <title>Five Months in the making, multi-architecture builder</title>
      <dc:creator>DaShaun</dc:creator>
      <pubDate>Mon, 18 Sep 2023 00:09:00 +0000</pubDate>
      <link>https://dev.to/dashaun/five-months-in-the-making-multi-architecture-builder-27oi</link>
      <guid>https://dev.to/dashaun/five-months-in-the-making-multi-architecture-builder-27oi</guid>
      <description>&lt;h2&gt;
  
  
  Long-overdue version of multi-architecture buildpack with Spring Boot 3 AOT processing support for M1, Raspberry Pi, ARM64
&lt;/h2&gt;

&lt;p&gt;If you have been following along, you are probably asking "What took so long?" and I totally understand.&lt;br&gt;
I had no idea what was wrong before today.&lt;br&gt;
My scripts had been working for over a year, then just stopped.&lt;br&gt;
I had removed a bunch of the debug info from the logs, which didn't help the situation at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  Upstream progress
&lt;/h2&gt;

&lt;p&gt;The Paketo team continues to put a high priority on ARM64 and multi-architecture tools/buildpacks.&lt;br&gt;
I had created several ARM64 workarounds over the years.&lt;br&gt;
The Paketo team continued improving their tools to support ARM64 as well.&lt;br&gt;
I was not doing a good job of pulling the tooling changes from upstream.&lt;br&gt;
So my workarounds were breaking the build.&lt;/p&gt;

&lt;h2&gt;
  
  
  Several failed attempts
&lt;/h2&gt;

&lt;p&gt;I had made several solid attempts at resolving the issues.&lt;br&gt;
I just kept getting distracted with other priorities.&lt;br&gt;
I have had this process working on multiple environments.&lt;br&gt;
Every version of my pipeline seemed to be broken in a slightly different way.&lt;br&gt;
They were, because of my workarounds.&lt;br&gt;
I had different workarounds for different environments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Motivation
&lt;/h2&gt;

&lt;p&gt;This year, I've been traveling more than ever.&lt;br&gt;
In fact, I'm on my way to Oracle Cloud World 2023, right now.&lt;br&gt;
The laptop that I was using for work, wasn't keeping up with what I was needing to deliver, to the community.&lt;br&gt;
My conference sessions were having issues, my customer sessions were also having issues.&lt;br&gt;
Watching for over 4-minutes, for a native compile, isn't great for anyone.&lt;br&gt;
So I requested a new M1 laptop.&lt;br&gt;
When I got home on Friday, my new laptop had arrived.&lt;br&gt;
Since I'm going to be working on an M1 from now on, I needed to have all the fast builds.&lt;br&gt;
I needed to make sure all of my demos work on the new hardware.&lt;br&gt;
I also needed to be ready to use Java 21 when it comes out in 2 days.&lt;br&gt;
In addition to my new laptop from work, my Ampere Dev Kit server also arrived.&lt;br&gt;
The server had been stuck in US Customs for a couple of months, after arriving from China.&lt;br&gt;
It was my first time working with US Customs.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;M1&lt;/li&gt;
&lt;li&gt;Ampere Dev Kit&lt;/li&gt;
&lt;li&gt;Java 21&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The work
&lt;/h2&gt;

&lt;p&gt;On my new M1, I simply started adding in debug info to the scripts.&lt;br&gt;
It felt good to get back into the process and remind myself how it all worked.&lt;br&gt;
When things work, you don't need to keep the details in your mental cache.&lt;br&gt;
The upstream builder added a few buildpacks.&lt;br&gt;
Packaging the newer buildpacks is where the problems were.&lt;br&gt;
Moments ago, I was getting ready to board when my tests passed.&lt;br&gt;
The KC Chiefs were playing Jacksonville at the same time.&lt;br&gt;
When I shouted with excitement, the people around me assumed that I was watching the game.&lt;br&gt;
I love my job.&lt;br&gt;
I love what I do.&lt;br&gt;
I could hear the Chiefs game, but getting this build to work was even more exciting that watching the Chiefs win the Super Bowl, twice.&lt;/p&gt;

&lt;h2&gt;
  
  
  A huge surprise
&lt;/h2&gt;

&lt;p&gt;I don't remember what the number was, from the last time I looked at the stats on Docker Hub.&lt;br&gt;
When I went to look today, I was hoping for it to be over 2000.&lt;br&gt;
Maybe 3000?  Is 5000 possible?&lt;br&gt;
My goosebumps hit when I saw that it was over 8500!&lt;br&gt;
I am so excited that people are using it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;p&gt;I am going to try to move away from my scripts, towards what the Paketo team is doing.&lt;br&gt;
I want to get my version moved over to the &lt;a href="https://github.com/paketo-community"&gt;paketo-community&lt;/a&gt; repository.&lt;br&gt;
Hopefully, from there, it will be even closer to being merged upstream.&lt;/p&gt;

&lt;p&gt;Stay tuned!&lt;/p&gt;

&lt;h2&gt;
  
  
  Feedback welcome
&lt;/h2&gt;

&lt;p&gt;Please let me know how it is working for you, all of &lt;a href="https://dashaun.com"&gt;my socials/contact info can be found here&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>arm64</category>
      <category>multiarch</category>
      <category>buildpacks</category>
      <category>spring</category>
    </item>
    <item>
      <title>Teamwork makes the dream work for this multi-architecture builder.</title>
      <dc:creator>DaShaun</dc:creator>
      <pubDate>Tue, 13 Dec 2022 04:28:48 +0000</pubDate>
      <link>https://dev.to/dashaun/teamwork-makes-the-dream-work-for-this-multi-architecture-builder-1co8</link>
      <guid>https://dev.to/dashaun/teamwork-makes-the-dream-work-for-this-multi-architecture-builder-1co8</guid>
      <description>&lt;h1&gt;
  
  
  Teamwork makes the dream work for this multi-architecture builder
&lt;/h1&gt;

&lt;p&gt;There has never been a better time to start delivering Spring Boot applications to ARM64 architectures. I've been working on ARM64 buildpacks for over a year now and I want you to see how far things have come.  These last few days have been leveled-up with help from Daniel Mikusa and Salman Malik, and the results are amazing.&lt;/p&gt;

&lt;h2&gt;
  
  
  So close
&lt;/h2&gt;

&lt;p&gt;I'm excited because the builder for ARM64 is so close to the AMD64 builder supported by Paketo.  To see just how close things are, we can do a simple &lt;code&gt;diff&lt;/code&gt; of the builders, from different architectures.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Optionally, clear cached images first&lt;/p&gt;


&lt;pre class="highlight shell"&gt;&lt;code&gt;docker system prune &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nt"&gt;--volumes&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Inspect with ARM64
&lt;/h2&gt;

&lt;p&gt;On an ARM64 device:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pack builder inspect dashaun/builder:tiny &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; ~/dashaun.builder.tiny.arm64
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The file should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Inspecting builder: dashaun/builder:tiny

REMOTE:

Description: Tiny base image (bionic build image, distroless-like run image) with buildpacks for Java, Java Native Image and Go

Created By:
Name: Pack CLI
Version: 0.28.0+git-b10e41f.build-3729

Trusted: No

Stack:
ID: io.paketo.stacks.tiny

Lifecycle:
Version: 0.15.2
Buildpack APIs:
Deprecated: (none)
Supported: 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9
Platform APIs:
Deprecated: (none)
Supported: 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 0.10

Run Images:
index.docker.io/paketobuildpacks/run:tiny-cnb
gcr.io/paketo-buildpacks/run:tiny-cnb

Buildpacks:
ID                                                  NAME                                                   VERSION        HOMEPAGE
paketo-buildpacks/apache-tomcat                     Paketo Buildpack for Apache Tomcat                     7.9.1          https://github.com/paketo-buildpacks/apache-tomcat
paketo-buildpacks/apache-tomee                      Paketo Buildpack for Apache Tomee                      1.4.0          https://github.com/paketo-buildpacks/apache-tomee
paketo-buildpacks/azure-application-insights        Paketo Buildpack for Azure Application Insights        5.9.3          https://github.com/paketo-buildpacks/azure-application-insights
paketo-buildpacks/bellsoft-liberica                 Paketo Buildpack for BellSoft Liberica                 9.10.1         https://github.com/paketo-buildpacks/bellsoft-liberica
paketo-buildpacks/ca-certificates                   Paketo Buildpack for CA Certificates                   3.5.1          https://github.com/paketo-buildpacks/ca-certificates
paketo-buildpacks/clojure-tools                     Paketo Buildpack for Clojure Tools                     2.6.1          https://github.com/paketo-buildpacks/clojure-tools
paketo-buildpacks/datadog                           Paketo Buildpack for Datadog                           3.1.0          https://github.com/paketo-buildpacks/datadog
paketo-buildpacks/dist-zip                          Paketo Buildpack for DistZip                           5.4.0          https://github.com/paketo-buildpacks/dist-zip
paketo-buildpacks/encrypt-at-rest                   Paketo Buildpack for Encrypt-at-Rest                   4.3.2          https://github.com/paketo-buildpacks/encrypt-at-rest
paketo-buildpacks/environment-variables             Paketo Buildpack for Environment Variables             4.4.0          https://github.com/paketo-buildpacks/environment-variables
paketo-buildpacks/executable-jar                    Paketo Buildpack for Executable JAR                    6.5.0          https://github.com/paketo-buildpacks/executable-jar
paketo-buildpacks/git                               Paketo Buildpack for Git                               1.0.0          https://github.com/paketo-buildpacks/git
paketo-buildpacks/go                                Paketo Buildpack for Go                                4.0.0          https://github.com/paketo-buildpacks/go
paketo-buildpacks/go-build                          Paketo Buildpack for Go Build                          2.0.7          https://github.com/paketo-buildpacks/go-build
paketo-buildpacks/go-dist                           Paketo Buildpack for Go Distribution                   2.2.2          https://github.com/paketo-buildpacks/go-dist
paketo-buildpacks/go-mod-vendor                     Paketo Buildpack for Go Mod Vendor                     1.0.6          https://github.com/paketo-buildpacks/go-mod-vendor
paketo-buildpacks/google-stackdriver                Paketo Buildpack for Google Stackdriver                7.0.0          https://github.com/paketo-buildpacks/google-stackdriver
paketo-buildpacks/gradle                            Paketo Buildpack for Gradle                            6.10.0         https://github.com/paketo-buildpacks/gradle
paketo-buildpacks/image-labels                      Paketo Buildpack for Image Labels                      4.3.0          https://github.com/paketo-buildpacks/image-labels
paketo-buildpacks/jattach                           Paketo Buildpack for JAttach                           1.2.0          https://github.com/paketo-buildpacks/jattach
paketo-buildpacks/java                              Paketo Buildpack for Java                              8.1.0          https://paketo.io/docs/howto/java
paketo-buildpacks/java-memory-assistant             Paketo Buildpack for Java Memory Assistant             1.2.0          https://github.com/paketo-buildpacks/java-memory-assistant
paketo-buildpacks/java-native-image                 Paketo Buildpack for Java Native Image                 7.42.0         https://paketo.io/docs/howto/java/#build-an-app-as-a-graalvm-native-image-application
paketo-buildpacks/leiningen                         Paketo Buildpack for Leiningen                         4.4.0          https://github.com/paketo-buildpacks/leiningen
paketo-buildpacks/liberty                           Paketo Buildpack for Liberty                           3.1.0          https://github.com/paketo-buildpacks/liberty
paketo-buildpacks/maven                             Paketo Buildpack for Maven                             6.11.0         https://github.com/paketo-buildpacks/maven
paketo-buildpacks/native-image                      Paketo Buildpack for Native Image                      5.6.0          https://github.com/paketo-buildpacks/native-image
paketo-buildpacks/procfile                          Paketo Buildpack for Procfile                          5.5.0          https://github.com/paketo-buildpacks/procfile
paketo-buildpacks/sbt                               Paketo Buildpack for SBT                               6.9.0          https://github.com/paketo-buildpacks/sbt
paketo-buildpacks/spring-boot                       Paketo Buildpack for Spring Boot                       5.20.0         https://github.com/paketo-buildpacks/spring-boot
paketo-buildpacks/syft                              Paketo Buildpack for Syft                              1.23.0         https://github.com/paketo-buildpacks/syft
paketo-buildpacks/upx                               Paketo Buildpack for UPX                               3.3.0          https://github.com/paketo-buildpacks/upx
paketo-buildpacks/watchexec                         Paketo Buildpack for Watchexec                         2.7.0          https://github.com/paketo-buildpacks/watchexec

Detection Order:
├ Group #1:
│  └ paketo-buildpacks/java-native-image@7.42.0
│     └ Group #1:
│        ├ paketo-buildpacks/ca-certificates@3.5.1          (optional)
│        ├ paketo-buildpacks/upx@3.3.0                      (optional)
│        ├ paketo-buildpacks/bellsoft-liberica@9.10.1
│        ├ paketo-buildpacks/syft@1.23.0                    (optional)
│        ├ paketo-buildpacks/leiningen@4.4.0                (optional)
│        ├ paketo-buildpacks/gradle@6.10.0                  (optional)
│        ├ paketo-buildpacks/maven@6.11.0                   (optional)
│        ├ paketo-buildpacks/sbt@6.9.0                      (optional)
│        ├ paketo-buildpacks/executable-jar@6.5.0           (optional)
│        ├ paketo-buildpacks/spring-boot@5.20.0             (optional)
│        ├ paketo-buildpacks/native-image@5.6.0
│        ├ paketo-buildpacks/procfile@5.5.0                 (optional)
│        ├ paketo-buildpacks/environment-variables@4.4.0    (optional)
│        └ paketo-buildpacks/image-labels@4.3.0             (optional)
├ Group #2:
│  └ paketo-buildpacks/java@8.1.0
│     └ Group #1:
│        ├ paketo-buildpacks/ca-certificates@3.5.1               (optional)
│        ├ paketo-buildpacks/bellsoft-liberica@9.10.1
│        ├ paketo-buildpacks/syft@1.23.0                         (optional)
│        ├ paketo-buildpacks/leiningen@4.4.0                     (optional)
│        ├ paketo-buildpacks/clojure-tools@2.6.1                 (optional)
│        ├ paketo-buildpacks/gradle@6.10.0                       (optional)
│        ├ paketo-buildpacks/maven@6.11.0                        (optional)
│        ├ paketo-buildpacks/sbt@6.9.0                           (optional)
│        ├ paketo-buildpacks/watchexec@2.7.0                     (optional)
│        ├ paketo-buildpacks/executable-jar@6.5.0                (optional)
│        ├ paketo-buildpacks/apache-tomcat@7.9.1                 (optional)
│        ├ paketo-buildpacks/apache-tomee@1.4.0                  (optional)
│        ├ paketo-buildpacks/liberty@3.1.0                       (optional)
│        ├ paketo-buildpacks/dist-zip@5.4.0                      (optional)
│        ├ paketo-buildpacks/spring-boot@5.20.0                  (optional)
│        ├ paketo-buildpacks/procfile@5.5.0                      (optional)
│        ├ paketo-buildpacks/jattach@1.2.0                       (optional)
│        ├ paketo-buildpacks/azure-application-insights@5.9.3    (optional)
│        ├ paketo-buildpacks/google-stackdriver@7.0.0            (optional)
│        ├ paketo-buildpacks/datadog@3.1.0                       (optional)
│        ├ paketo-buildpacks/java-memory-assistant@1.2.0         (optional)
│        ├ paketo-buildpacks/encrypt-at-rest@4.3.2               (optional)
│        ├ paketo-buildpacks/environment-variables@4.4.0         (optional)
│        └ paketo-buildpacks/image-labels@4.3.0                  (optional)
├ Group #3:
│  ├ paketo-buildpacks/go@4.0.0
│  │  └ Group #1:
│  │     ├ paketo-buildpacks/ca-certificates@3.5.1          (optional)
│  │     ├ paketo-buildpacks/watchexec@2.7.0                (optional)
│  │     ├ paketo-buildpacks/go-dist@2.2.2
│  │     ├ paketo-buildpacks/git@1.0.0                      (optional)
│  │     ├ paketo-buildpacks/go-mod-vendor@1.0.6
│  │     ├ paketo-buildpacks/go-build@2.0.7
│  │     ├ paketo-buildpacks/procfile@5.5.0                 (optional)
│  │     ├ paketo-buildpacks/environment-variables@4.4.0    (optional)
│  │     └ paketo-buildpacks/image-labels@4.3.0             (optional)
│  └ paketo-buildpacks/go@4.0.0
│     └ Group #2:
│        ├ paketo-buildpacks/ca-certificates@3.5.1          (optional)
│        ├ paketo-buildpacks/watchexec@2.7.0                (optional)
│        ├ paketo-buildpacks/go-dist@2.2.2
│        ├ paketo-buildpacks/git@1.0.0                      (optional)
│        ├ paketo-buildpacks/go-build@2.0.7
│        ├ paketo-buildpacks/procfile@5.5.0                 (optional)
│        ├ paketo-buildpacks/environment-variables@4.4.0    (optional)
│        └ paketo-buildpacks/image-labels@4.3.0             (optional)
└ Group #4:
└ paketo-buildpacks/procfile@5.5.0

LOCAL:
(not present)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Inspect with AMD64
&lt;/h2&gt;

&lt;p&gt;On an AMD64 device:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pack builder inspect dashaun/builder:tiny &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; ~/dashaun.builder.tiny.amd64
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The file will look very similar:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Inspecting builder: dashaun/builder:tiny

REMOTE:

Description: Tiny base image (bionic build image, distroless-like run image) with buildpacks for Java, Java Native Image and Go

Created By:
  Name: Pack CLI
  Version: 0.28.0+git-b10e41f.build-3729

Trusted: No

Stack:
  ID: io.paketo.stacks.tiny

Lifecycle:
  Version: 0.15.2
  Buildpack APIs:
    Deprecated: (none)
    Supported: 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9
  Platform APIs:
    Deprecated: (none)
    Supported: 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 0.10

Run Images:
  index.docker.io/paketobuildpacks/run:tiny-cnb
  gcr.io/paketo-buildpacks/run:tiny-cnb

Buildpacks:
  ID                                                  NAME                                                   VERSION        HOMEPAGE
  paketo-buildpacks/apache-tomcat                     Paketo Buildpack for Apache Tomcat                     7.9.1          https://github.com/paketo-buildpacks/apache-tomcat
  paketo-buildpacks/apache-tomee                      Paketo Buildpack for Apache Tomee                      1.4.0          https://github.com/paketo-buildpacks/apache-tomee
  paketo-buildpacks/azure-application-insights        Paketo Buildpack for Azure Application Insights        5.9.3          https://github.com/paketo-buildpacks/azure-application-insights
  paketo-buildpacks/bellsoft-liberica                 Paketo Buildpack for BellSoft Liberica                 9.10.1         https://github.com/paketo-buildpacks/bellsoft-liberica
  paketo-buildpacks/ca-certificates                   Paketo Buildpack for CA Certificates                   3.5.1          https://github.com/paketo-buildpacks/ca-certificates
  paketo-buildpacks/clojure-tools                     Paketo Buildpack for Clojure Tools                     2.6.1          https://github.com/paketo-buildpacks/clojure-tools
  paketo-buildpacks/datadog                           Paketo Buildpack for Datadog                           3.1.0          https://github.com/paketo-buildpacks/datadog
  paketo-buildpacks/dist-zip                          Paketo Buildpack for DistZip                           5.4.0          https://github.com/paketo-buildpacks/dist-zip
  paketo-buildpacks/encrypt-at-rest                   Paketo Buildpack for Encrypt-at-Rest                   4.3.2          https://github.com/paketo-buildpacks/encrypt-at-rest
  paketo-buildpacks/environment-variables             Paketo Buildpack for Environment Variables             4.4.0          https://github.com/paketo-buildpacks/environment-variables
  paketo-buildpacks/executable-jar                    Paketo Buildpack for Executable JAR                    6.5.0          https://github.com/paketo-buildpacks/executable-jar
  paketo-buildpacks/git                               Paketo Buildpack for Git                               1.0.0          https://github.com/paketo-buildpacks/git
  paketo-buildpacks/go                                Paketo Buildpack for Go                                4.0.0          https://github.com/paketo-buildpacks/go
  paketo-buildpacks/go-build                          Paketo Buildpack for Go Build                          2.0.7          https://github.com/paketo-buildpacks/go-build
  paketo-buildpacks/go-dist                           Paketo Buildpack for Go Distribution                   2.2.2          https://github.com/paketo-buildpacks/go-dist
  paketo-buildpacks/go-mod-vendor                     Paketo Buildpack for Go Mod Vendor                     1.0.6          https://github.com/paketo-buildpacks/go-mod-vendor
  paketo-buildpacks/google-stackdriver                Paketo Buildpack for Google Stackdriver                7.0.0          https://github.com/paketo-buildpacks/google-stackdriver
  paketo-buildpacks/gradle                            Paketo Buildpack for Gradle                            6.10.0         https://github.com/paketo-buildpacks/gradle
  paketo-buildpacks/image-labels                      Paketo Buildpack for Image Labels                      4.3.0          https://github.com/paketo-buildpacks/image-labels
  paketo-buildpacks/jattach                           Paketo Buildpack for JAttach                           1.2.0          https://github.com/paketo-buildpacks/jattach
  paketo-buildpacks/java                              Paketo Buildpack for Java                              8.1.0          https://paketo.io/docs/howto/java
  paketo-buildpacks/java-memory-assistant             Paketo Buildpack for Java Memory Assistant             1.2.0          https://github.com/paketo-buildpacks/java-memory-assistant
  paketo-buildpacks/java-native-image                 Paketo Buildpack for Java Native Image                 7.42.0         https://paketo.io/docs/howto/java/#build-an-app-as-a-graalvm-native-image-application
  paketo-buildpacks/leiningen                         Paketo Buildpack for Leiningen                         4.4.0          https://github.com/paketo-buildpacks/leiningen
  paketo-buildpacks/liberty                           Paketo Buildpack for Liberty                           3.1.0          https://github.com/paketo-buildpacks/liberty
  paketo-buildpacks/maven                             Paketo Buildpack for Maven                             6.11.0         https://github.com/paketo-buildpacks/maven
  paketo-buildpacks/native-image                      Paketo Buildpack for Native Image                      5.6.0          https://github.com/paketo-buildpacks/native-image
  paketo-buildpacks/procfile                          Paketo Buildpack for Procfile                          5.5.0          https://github.com/paketo-buildpacks/procfile
  paketo-buildpacks/sbt                               Paketo Buildpack for SBT                               6.9.0          https://github.com/paketo-buildpacks/sbt
  paketo-buildpacks/spring-boot                       Paketo Buildpack for Spring Boot                       5.20.0         https://github.com/paketo-buildpacks/spring-boot
  paketo-buildpacks/syft                              Paketo Buildpack for Syft                              1.23.0         https://github.com/paketo-buildpacks/syft
  paketo-buildpacks/upx                               Paketo Buildpack for UPX                               3.3.0          https://github.com/paketo-buildpacks/upx
  paketo-buildpacks/watchexec                         Paketo Buildpack for Watchexec                         2.7.0          https://github.com/paketo-buildpacks/watchexec

Detection Order:
 ├ Group #1:
 │  └ paketo-buildpacks/java-native-image@7.42.0
 │     └ Group #1:
 │        ├ paketo-buildpacks/ca-certificates@3.5.1          (optional)
 │        ├ paketo-buildpacks/upx@3.3.0                      (optional)
 │        ├ paketo-buildpacks/bellsoft-liberica@9.10.1
 │        ├ paketo-buildpacks/syft@1.23.0                    (optional)
 │        ├ paketo-buildpacks/leiningen@4.4.0                (optional)
 │        ├ paketo-buildpacks/gradle@6.10.0                  (optional)
 │        ├ paketo-buildpacks/maven@6.11.0                   (optional)
 │        ├ paketo-buildpacks/sbt@6.9.0                      (optional)
 │        ├ paketo-buildpacks/executable-jar@6.5.0           (optional)
 │        ├ paketo-buildpacks/spring-boot@5.20.0             (optional)
 │        ├ paketo-buildpacks/native-image@5.6.0
 │        ├ paketo-buildpacks/procfile@5.5.0                 (optional)
 │        ├ paketo-buildpacks/environment-variables@4.4.0    (optional)
 │        └ paketo-buildpacks/image-labels@4.3.0             (optional)
 ├ Group #2:
 │  └ paketo-buildpacks/java@8.1.0
 │     └ Group #1:
 │        ├ paketo-buildpacks/ca-certificates@3.5.1               (optional)
 │        ├ paketo-buildpacks/bellsoft-liberica@9.10.1
 │        ├ paketo-buildpacks/syft@1.23.0                         (optional)
 │        ├ paketo-buildpacks/leiningen@4.4.0                     (optional)
 │        ├ paketo-buildpacks/clojure-tools@2.6.1                 (optional)
 │        ├ paketo-buildpacks/gradle@6.10.0                       (optional)
 │        ├ paketo-buildpacks/maven@6.11.0                        (optional)
 │        ├ paketo-buildpacks/sbt@6.9.0                           (optional)
 │        ├ paketo-buildpacks/watchexec@2.7.0                     (optional)
 │        ├ paketo-buildpacks/executable-jar@6.5.0                (optional)
 │        ├ paketo-buildpacks/apache-tomcat@7.9.1                 (optional)
 │        ├ paketo-buildpacks/apache-tomee@1.4.0                  (optional)
 │        ├ paketo-buildpacks/liberty@3.1.0                       (optional)
 │        ├ paketo-buildpacks/dist-zip@5.4.0                      (optional)
 │        ├ paketo-buildpacks/spring-boot@5.20.0                  (optional)
 │        ├ paketo-buildpacks/procfile@5.5.0                      (optional)
 │        ├ paketo-buildpacks/jattach@1.2.0                       (optional)
 │        ├ paketo-buildpacks/azure-application-insights@5.9.3    (optional)
 │        ├ paketo-buildpacks/google-stackdriver@7.0.0            (optional)
 │        ├ paketo-buildpacks/datadog@3.1.0                       (optional)
 │        ├ paketo-buildpacks/java-memory-assistant@1.2.0         (optional)
 │        ├ paketo-buildpacks/encrypt-at-rest@4.3.2               (optional)
 │        ├ paketo-buildpacks/environment-variables@4.4.0         (optional)
 │        └ paketo-buildpacks/image-labels@4.3.0                  (optional)
 ├ Group #3:
 │  ├ paketo-buildpacks/go@4.0.0
 │  │  └ Group #1:
 │  │     ├ paketo-buildpacks/ca-certificates@3.5.1          (optional)
 │  │     ├ paketo-buildpacks/watchexec@2.7.0                (optional)
 │  │     ├ paketo-buildpacks/go-dist@2.2.2
 │  │     ├ paketo-buildpacks/git@1.0.0                      (optional)
 │  │     ├ paketo-buildpacks/go-mod-vendor@1.0.6
 │  │     ├ paketo-buildpacks/go-build@2.0.7
 │  │     ├ paketo-buildpacks/procfile@5.5.0                 (optional)
 │  │     ├ paketo-buildpacks/environment-variables@4.4.0    (optional)
 │  │     └ paketo-buildpacks/image-labels@4.3.0             (optional)
 │  └ paketo-buildpacks/go@4.0.0
 │     └ Group #2:
 │        ├ paketo-buildpacks/ca-certificates@3.5.1          (optional)
 │        ├ paketo-buildpacks/watchexec@2.7.0                (optional)
 │        ├ paketo-buildpacks/go-dist@2.2.2
 │        ├ paketo-buildpacks/git@1.0.0                      (optional)
 │        ├ paketo-buildpacks/go-build@2.0.7
 │        ├ paketo-buildpacks/procfile@5.5.0                 (optional)
 │        ├ paketo-buildpacks/environment-variables@4.4.0    (optional)
 │        └ paketo-buildpacks/image-labels@4.3.0             (optional)
 └ Group #4:
    └ paketo-buildpacks/procfile@5.5.0

LOCAL:
(not present)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, take a look at the &lt;code&gt;diff&lt;/code&gt; between those two files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;diff dashaun.builder.tiny.arm64 dashaun.builder.tiny.amd64
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As I'm writing this, the &lt;code&gt;diff&lt;/code&gt; looks like this:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
text
&amp;lt; Inspecting builder: 'dashaun/builder:tiny'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>webdev</category>
      <category>mobile</category>
      <category>softwaredevelopment</category>
      <category>devops</category>
    </item>
    <item>
      <title>Spring Cloud Gateway 4.0.0-RC2 native example with Testcontainers</title>
      <dc:creator>DaShaun</dc:creator>
      <pubDate>Fri, 09 Dec 2022 01:24:19 +0000</pubDate>
      <link>https://dev.to/dashaun/spring-cloud-gateway-400-rc2-native-example-with-testcontainers-1jpg</link>
      <guid>https://dev.to/dashaun/spring-cloud-gateway-400-rc2-native-example-with-testcontainers-1jpg</guid>
      <description>&lt;h1&gt;
  
  
  Spring Cloud Gateway 4.0.0-RC2 native example with Testcontainers
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://github.com/dashaun/dev.dashaun.service.gateway" rel="noopener noreferrer"&gt;This example repository is on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are 3 goals I have for this example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deliver Spring Cloud Gateway as a graalvm-native-image with Spring Boot 3&lt;/li&gt;
&lt;li&gt;Get feedback on the multi-architecture buildpack that includes ARM64 support&lt;/li&gt;
&lt;li&gt;Demonstrate a pattern for building and testing buildpack images using Testcontainers&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://spring.io/projects/spring-cloud-gateway" rel="noopener noreferrer"&gt;Spring Cloud Gateway&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;From the &lt;a href="https://spring.io/projects/spring-cloud-gateway" rel="noopener noreferrer"&gt;Spring.io&lt;/a&gt; website:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Spring Cloud Gateway aims to provide a simple,
yet effective way to route to APIs and provide
cross cutting concerns to them such as:
security, monitoring/metrics, and resiliency.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is very popular, and even more valuable than it is popular!&lt;/p&gt;

&lt;p&gt;Spring Framework 6 and Spring Boot 3.0.0 have recently gone GA. My favorite feature of these new releases is hands down the AOT processing. The AOT processing, provided by &lt;a href="https://graalvm.org" rel="noopener noreferrer"&gt;GraalVM&lt;/a&gt;, creates statically linked binary images. These images are optimized, ahead of time, for the operating system and architecture. For many modern workloads they can provide smaller image sizes, smaller memory footprint, and a much faster startup time.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dashaun.com/posts/k3s-knative-ubuntu-raspberry-pi/" rel="noopener noreferrer"&gt;In a recent experiment&lt;/a&gt;, I was able to expose Spring Cloud Gateway to the public, and have it route to other Spring Boot 3 native image workloads. &lt;/p&gt;

&lt;p&gt;In that experiment Spring Cloud Gateway provided:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Single service for SSL and ingress integration&lt;/li&gt;
&lt;li&gt;YAML configured routing&lt;/li&gt;
&lt;li&gt;Circuit Breaker capabilities with Resilience4j&lt;/li&gt;
&lt;li&gt;Request Rate Limiting with Redis&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the experiment, I left the Spring Cloud Gateway running, by setting the &lt;code&gt;min-scale&lt;/code&gt; value to 1.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;spring-cloud-gateway&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;autoscaling.knative.dev/initial-scale&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1"&lt;/span&gt;
        &lt;span class="na"&gt;autoscaling.knative.dev/min-scale&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1"&lt;/span&gt;
        &lt;span class="na"&gt;autoscaling.knative.dev/max-scale&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2"&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dashaun/dev.dashaun.service.gateway:latest&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The other Spring Boot applications, being routed to by the gateway, were allowed to scale-to-zero when they weren't used.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dashaun.com/posts/k3s-knative-ubuntu-raspberry-pi/" rel="noopener noreferrer"&gt;The experiment was on a cluster of Raspberry Pi&lt;/a&gt; devices which are ARM64 cpus.&lt;/p&gt;

&lt;p&gt;The Spring Cloud Gateway, native image, typically starts up between 0.600s and 0.650s, on a Raspberry Pi.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dashaun.com/posts/spring-cloud-gateway-4-0-0-rc2-native-example-with-testcontainers/images/scg-startup-rpi.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fspdl5fw2vxqmdsxihjdg.png" alt="SCG native image Raspberry Pi startup" width="800" height="428"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Today, the buildpack delivered by &lt;a href="https://start.spring.io" rel="noopener noreferrer"&gt;Spring Initializr at start.spring.io&lt;/a&gt;, is unable to create native images on ARM64.&lt;/p&gt;

&lt;h2&gt;
  
  
  Multi-architecture buildpack
&lt;/h2&gt;

&lt;p&gt;Earlier this year, I started working on a buildpack to use with the experimental project, &lt;code&gt;Spring Native&lt;/code&gt;, and ARM64, for my Raspberry Pi devices. Later the M1/M2 Apple Silicon community, also ARM64, began showing interest in using this buildpack. &lt;a href="https://hub.docker.com/r/dashaun/java-native-builder-arm64" rel="noopener noreferrer"&gt;I have delivered a few versions to date&lt;/a&gt;. I've also created some automation to improve my process for creating these buildpacks.&lt;/p&gt;

&lt;p&gt;Recently, I created a &lt;a href="https://hub.docker.com/r/dashaun/java-native-builder-multiarch" rel="noopener noreferrer"&gt;multi-architecture buildpack&lt;/a&gt; that can be used with Spring Boot 3, to create native images on both Intel/AMD64 and ARM64 architectures. This example repository is configured with this multi-architecture buildpack.&lt;/p&gt;

&lt;p&gt;I'm still working to improve this builder, in an effort to get ARM64 support upstream to &lt;a href="https://paketo.io" rel="noopener noreferrer"&gt;Paketo&lt;/a&gt;. I have a long way to go, but with your help, I can discover gaps and continue this effort. One of the known, and frustrating gaps today, is that UPX compression does not work on ARM64, it does work with AMD64.  I really want to get this figured out, and it is a high priority for me.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building and testing native images
&lt;/h2&gt;

&lt;p&gt;One of the most common concerns I have heard from the community this year has been around validating that the native image artifacts are delivering the same results as the JIT/JVM based artifacts. This is a very valid concern.  This is also why I feel, that organizations with mature testing capabilities today, will be able to move forward with native images faster.&lt;/p&gt;

&lt;p&gt;This repository provides a &lt;code&gt;BuildImageTest&lt;/code&gt; that uses the buildpack to create a native image.  It then tests the native image, using &lt;a href="https://testcontainers.org" rel="noopener noreferrer"&gt;Testcontainers&lt;/a&gt; and &lt;a href="https://junit.org" rel="noopener noreferrer"&gt;JUnit&lt;/a&gt;. Building the native image with AOT processing, as part of a test, takes minutes not seconds, and should not be part of normal "inner loop" development.  So the &lt;code&gt;BuildImageTest&lt;/code&gt; is in a separate &lt;code&gt;sourceSet&lt;/code&gt; and can be executed independently. This is a very powerful pattern, that I'm just getting started with. I would love to hear your thoughts on this pattern or other alternatives to it.&lt;/p&gt;

&lt;p&gt;To build and validate the native image:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./gradlew integrationTest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;You can also execute the &lt;code&gt;BuildImageTest&lt;/code&gt; directly in your IDE.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Thanks
&lt;/h2&gt;

&lt;p&gt;I am having so much fun with all of these new capabilities and challenges. I want to thank the Spring team, for all of their amazing work bring Spring Framework 6 and Spring Boot 3 to GA. I also want to thank Daniel Mikusa and the Paketo community for being amazing to work with. I want to thank Sergei Egorov for &lt;a href="https://bsideup.github.io/posts/spring_boot_in_container/" rel="noopener noreferrer"&gt;sharing the testing pattern&lt;/a&gt; and supporting my Testcontainers journey. Finally, I would like to thank &lt;a href="https://danvega.dev" rel="noopener noreferrer"&gt;Dan Vega&lt;/a&gt;, my &lt;a href="http://bit.ly/spring-office-hours" rel="noopener noreferrer"&gt;Spring Office Hours&lt;/a&gt; cohost, for being an amazing teammate, and having the JUICE.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep Learning
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://spring.io/projects/spring-cloud-gateway" rel="noopener noreferrer"&gt;Spring Boot Gateway&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://spring.io/projects/spring-cloud" rel="noopener noreferrer"&gt;Spring Cloud 2022.0.0-RC2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://spring.io/projects/spring-boot" rel="noopener noreferrer"&gt;Spring Boot 3.0.0&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://testcontainers.org" rel="noopener noreferrer"&gt;Testcontainers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dashaun.com/posts/spring-boot-3-buildpacks-with-testcontainers-cloud/" rel="noopener noreferrer"&gt;Spring Boot 3 buildpacks with Testcontainers Cloud&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dashaun.com/posts/java-native-builder-multiarch-7-41-0/" rel="noopener noreferrer"&gt;Cloud Native Buildpack for ARM64 and AMD64&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dashaun.com/posts/multiarch-builder-poc/" rel="noopener noreferrer"&gt;First attempt at a multi-architecture buildpack&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dashaun.com/posts/k3s-knative-ubuntu-raspberry-pi/" rel="noopener noreferrer"&gt;K3s Knative Ubuntu Raspberry Pi&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>watercooler</category>
    </item>
    <item>
      <title>Cloud Native Buildpack for ARM64 and AMD64</title>
      <dc:creator>DaShaun</dc:creator>
      <pubDate>Tue, 06 Dec 2022 22:31:40 +0000</pubDate>
      <link>https://dev.to/dashaun/cloud-native-buildpack-for-arm64-and-amd64-39g2</link>
      <guid>https://dev.to/dashaun/cloud-native-buildpack-for-arm64-and-amd64-39g2</guid>
      <description>&lt;h1&gt;
  
  
  Version 7.41.0
&lt;/h1&gt;

&lt;p&gt;This builder works with Intel/AMD architectures and ARM64 architectures like Apple M1 and Raspberry Pi.&lt;/p&gt;

&lt;p&gt;With this release I was able to use a little more automation on my side, to get my &lt;code&gt;unsupported&lt;/code&gt; builder even closer to the &lt;a href="https://paketo.io" rel="noopener noreferrer"&gt;upstream at Paketo&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://hub.docker.com/r/dashaun/java-native-builder-multiarch" rel="noopener noreferrer"&gt;Docker&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/dmikusa/paketo-arm64" rel="noopener noreferrer"&gt;Daniel Mikusa GitHub Repo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Roadmap
&lt;/h2&gt;

&lt;p&gt;I'm trying to make each of the buildpacks as similar as possible to each other and upstream.&lt;br&gt;
I'm also working on delivering newer versions of the stack, which is currently Ubuntu Focal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Feedback welcome
&lt;/h2&gt;

&lt;p&gt;Please let me know how it is working for you, all of &lt;a href="https://dashaun.com" rel="noopener noreferrer"&gt;my socials/contact info can be found here&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>githubcopilot</category>
      <category>github</category>
    </item>
    <item>
      <title>K3s Knative Ubuntu Raspberry Pi</title>
      <dc:creator>DaShaun</dc:creator>
      <pubDate>Tue, 06 Dec 2022 07:58:45 +0000</pubDate>
      <link>https://dev.to/dashaun/k3s-knative-ubuntu-raspberry-pi-45i0</link>
      <guid>https://dev.to/dashaun/k3s-knative-ubuntu-raspberry-pi-45i0</guid>
      <description>&lt;h1&gt;
  
  
  K3s Knative Ubuntu Raspberry Pi
&lt;/h1&gt;

&lt;p&gt;Knative is a super exciting solution for serverless and event driven applications.&lt;br&gt;
Personally, I have been using it as a part of my &lt;code&gt;inner loop&lt;/code&gt; development.&lt;br&gt;
I can deploy dozens of workloads.&lt;br&gt;
All scaled down to zero when they aren't being used.&lt;br&gt;
All responding and starting up quickly when they are needed.&lt;/p&gt;

&lt;p&gt;I ran into a couple of problems with Envoy on Raspberry Pi OS.&lt;br&gt;
After investigating, it is a known issue in the Raspberry Pi OS kernel.&lt;br&gt;
In order to keep going forward with my project I had to choose between fixing the kernel or trying Ubuntu.&lt;/p&gt;

&lt;p&gt;This is an overview of how to get Knative Serving working on a Raspberry Pi device with K3s and Ubuntu.&lt;/p&gt;
&lt;h2&gt;
  
  
  Install Ubuntu Server
&lt;/h2&gt;

&lt;p&gt;Use RPI Imager.&lt;br&gt;
Select &lt;code&gt;Ubuntu Server 22.10 64-bit&lt;/code&gt; from the list of other distributions.&lt;br&gt;
Boot the new image.&lt;/p&gt;

&lt;p&gt;I was happy to see that SSH and the default user were configured as expected by using the advanced settings options.&lt;/p&gt;
&lt;h2&gt;
  
  
  Upgrade the stuff
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Grab the latest repository info&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update 
&lt;span class="c"&gt;# Install one prerequisite package that isn't installed by default&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;linux-modules-extra-raspi &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;span class="c"&gt;# Upgrade everything that can be upgraded&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt dist-upgrade &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Fix cgroup
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Add this to the beginning of /boot/firmware/cmdline.txt&lt;/span&gt;
&lt;span class="c"&gt;# cgroup_enable=cpuset cgroup_memory=1 cgroup_enable=memory&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;nano &lt;span class="nt"&gt;-w&lt;/span&gt; /boot/firmware/cmdline.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;Reboot after making this change.&lt;br&gt;
&lt;/p&gt;


&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;shutdown &lt;span class="nt"&gt;-r&lt;/span&gt; now
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Install k3s via k3sup
&lt;/h2&gt;

&lt;p&gt;From my laptop, not one of the Raspberry Pi nodes, run &lt;a href="https://github.com/alexellis/k3sup" rel="noopener noreferrer"&gt;k3sup&lt;/a&gt;, it's super easy.&lt;br&gt;
If you haven't figured it out by now, I'm a big fan of &lt;a href="https://github.com/alexellis/k3sup" rel="noopener noreferrer"&gt;k3sup&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;CONTROL_NODE_IP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;10.0.0.10
&lt;span class="c"&gt;# Disable Traefik because we will use kourier for knative&lt;/span&gt;
k3sup &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--ip&lt;/span&gt; &lt;span class="nv"&gt;$CONTROL_NODE_IP&lt;/span&gt; &lt;span class="nt"&gt;--user&lt;/span&gt; pi &lt;span class="nt"&gt;--k3s-extra-args&lt;/span&gt; &lt;span class="s1"&gt;'--disable traefik'&lt;/span&gt; &lt;span class="nt"&gt;--merge&lt;/span&gt; &lt;span class="nt"&gt;--local-path&lt;/span&gt; ~/.kube/config &lt;span class="nt"&gt;--context&lt;/span&gt; cluster00
&lt;span class="c"&gt;# Knative-Serving with Kourier works on a single Raspberry Pi 4 with 8GB&lt;/span&gt;
&lt;span class="c"&gt;# Optionally add other nodes&lt;/span&gt;
k3sup &lt;span class="nb"&gt;join&lt;/span&gt; &lt;span class="nt"&gt;--ip&lt;/span&gt; 10.0.0.11 &lt;span class="nt"&gt;--server-ip&lt;/span&gt; &lt;span class="nv"&gt;$CONTROL_NODE_IP&lt;/span&gt; &lt;span class="nt"&gt;--user&lt;/span&gt; pi
k3sup &lt;span class="nb"&gt;join&lt;/span&gt; &lt;span class="nt"&gt;--ip&lt;/span&gt; 10.0.0.12 &lt;span class="nt"&gt;--server-ip&lt;/span&gt; &lt;span class="nv"&gt;$CONTROL_NODE_IP&lt;/span&gt; &lt;span class="nt"&gt;--user&lt;/span&gt; pi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Deep breath
&lt;/h2&gt;

&lt;p&gt;This is where I struggled with how to deliver this example.&lt;br&gt;
The &lt;a href="https://knative.dev/docs/install/yaml-install/serving/install-serving-with-yaml/" rel="noopener noreferrer"&gt;Knative install documentation&lt;/a&gt; is awesome.&lt;br&gt;
I've been through it dozens of times!&lt;/p&gt;

&lt;p&gt;I tried to simplify this for you.&lt;br&gt;
I want to give you the 'easy button' experience.&lt;br&gt;
I created a &lt;a href="https://github.com/dashaun/knative-serving-raspberry-pi" rel="noopener noreferrer"&gt;repository&lt;/a&gt; with all the YAML you need to deploy Knative Serving to this K3s cluster.&lt;/p&gt;

&lt;h2&gt;
  
  
  Knative Serving with Kourier
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/dashaun/knative-serving-raspberry-pi
&lt;span class="nb"&gt;cd &lt;/span&gt;knative-serving-raspberry-pi/operator-serving-kourier
&lt;span class="c"&gt;# Use the `default` namespace in the Kubernetes cluster&lt;/span&gt;
kubectl config set-context &lt;span class="nt"&gt;--current&lt;/span&gt; &lt;span class="nt"&gt;--namespace&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;default
&lt;span class="c"&gt;# Deploy the Knative operator&lt;/span&gt;
kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; operator.yaml
&lt;span class="c"&gt;# Check the deployment status&lt;/span&gt;
kubectl get deployment knative-operator
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Continue when &lt;code&gt;Ready&lt;/code&gt; status is &lt;code&gt;1/1&lt;/code&gt;&lt;/p&gt;


&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NAME               READY   UP-TO-DATE   AVAILABLE   AGE
knative-operator   1/1     1            1           19h
&lt;/code&gt;&lt;/pre&gt;

&lt;/blockquote&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install Knative Serving custom resource and Kourier&lt;/span&gt;
kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; knative-serving.yaml
&lt;span class="c"&gt;# Check the custom resource status&lt;/span&gt;
kubectl get KnativeServing knative-serving &lt;span class="nt"&gt;-n&lt;/span&gt; knative-serving
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Continue when &lt;code&gt;READY&lt;/code&gt; is &lt;code&gt;True&lt;/code&gt;&lt;/p&gt;


&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NAME              VERSION   READY   REASON
knative-serving   1.8.0     True
&lt;/code&gt;&lt;/pre&gt;

&lt;/blockquote&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check the deployment status&lt;/span&gt;
kubectl get deployment &lt;span class="nt"&gt;-n&lt;/span&gt; knative-serving
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Continue when &lt;code&gt;READY&lt;/code&gt; are all &lt;code&gt;1/1&lt;/code&gt;&lt;/p&gt;


&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NAME                     READY   UP-TO-DATE   AVAILABLE   AGE
activator                1/1     1            1           23m
autoscaler               1/1     1            1           23m
controller               1/1     1            1           23m
domain-mapping           1/1     1            1           23m
domainmapping-webhook    1/1     1            1           23m
autoscaler-hpa           1/1     1            1           23m
3scale-kourier-gateway   1/1     1            1           23m
webhook                  1/1     1            1           23m
net-kourier-controller   1/1     1            1           23m
&lt;/code&gt;&lt;/pre&gt;

&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  SSLIP.IO and a Spring Boot 3 &lt;code&gt;native&lt;/code&gt; application
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install default domain&lt;/span&gt;
kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; serving-default-domain.yaml
&lt;span class="c"&gt;# Deploy a Spring Boot 3 `native` application&lt;/span&gt;
kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; spring-boot-native-pi-service.yaml
&lt;span class="c"&gt;# Get the address&lt;/span&gt;
kubectl get ksvc spring-boot-native-pi &lt;span class="nt"&gt;--output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;custom-columns&lt;span class="o"&gt;=&lt;/span&gt;NAME:.metadata.name,URL:.status.url
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;

&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NAME                    URL
spring-boot-native-pi   http://spring-boot-native-pi.default.10.0.0.10.sslip.io
&lt;/code&gt;&lt;/pre&gt;

&lt;/blockquote&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Curl the address provided by knative with `/actuator/health` at the end&lt;/span&gt;
curl http://spring-boot-native-pi.default.default.10.0.0.10.sslip.io/actuator/health
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Expected output:&lt;/p&gt;


&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{"status":"UP"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;With the recent release of Spring Boot 3.0.0, my Raspberry Pi collection has become significantly more interesting.&lt;br&gt;
Knative Serving allows me to deploy dozens of Spring Boot / Spring Cloud workloads to a tiny, low power device, with very exciting capabilities.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep Learning
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://knative.dev" rel="noopener noreferrer"&gt;Knative&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.spring.io/spring-boot/docs/current/reference/html/" rel="noopener noreferrer"&gt;Spring Boot&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/dashaun/knative-serving-raspberry-pi" rel="noopener noreferrer"&gt;Knative Serving Raspberry Pi&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>career</category>
      <category>community</category>
    </item>
    <item>
      <title>Spring Boot 3 Buildpacks with Testcontainers Cloud</title>
      <dc:creator>DaShaun</dc:creator>
      <pubDate>Tue, 22 Nov 2022 05:55:59 +0000</pubDate>
      <link>https://dev.to/dashaun/spring-boot-3-buildpacks-with-testcontainers-cloud-5fd2</link>
      <guid>https://dev.to/dashaun/spring-boot-3-buildpacks-with-testcontainers-cloud-5fd2</guid>
      <description>&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;I have a modest laptop. It checks the boxes, for just about everything I need to do for work. Just about. AOT processing, with GraalVM, is not a great experience on this machine.&lt;/p&gt;

&lt;h2&gt;
  
  
  The origin story
&lt;/h2&gt;

&lt;p&gt;Back in June 2022, I was presenting at SpringOne Tour NYC. As part of the demo, I built a &lt;code&gt;native&lt;/code&gt; image using Spring Boot 2.7 with the &lt;code&gt;spring-native&lt;/code&gt; experimental dependency. The session went well, except for the building the native image part of it. During my 50-minute session, my laptop was busy building the native image for over 8-minutes, using the buildpack. It was a humbling experience. After the session, I hung out with Sergei Egorov, and we worked on something very special. With help from &lt;a href="https://bsideup.github.io/posts/spring_boot_in_container/"&gt;one of his previous blog posts&lt;/a&gt;, we created a proof of concept. We used &lt;a href="https://www.testcontainers.cloud/"&gt;Testcontainers Cloud&lt;/a&gt; to build the native OCI image, in the cloud, in a little over 3-minutes. Pairing with Sergei is amazing, if you get the chance, I highly recommend it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://twitter.com/dashaun/status/1541812027286495235"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XevAkji7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dashaun.com/posts/spring-boot-3-buildpacks-with-testcontainers-cloud/images/origin-tweet.png" alt="Origin Story" width="800" height="1095"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  It sat on the back-burner and fire is hot
&lt;/h2&gt;

&lt;p&gt;I had this code sitting in my repository, waiting to be used. When it was time for SpringOne Tour Tel Aviv, Nov 2022, I was ready to show it off. Unfortunately for me, my session was shortened, so I pulled it out of the presentation. I shouldn’t have pulled it out, I made the exact same mistake that I made in NYC. When I built the native image using buildpacks, it took way too long on my laptop.&lt;/p&gt;

&lt;h2&gt;
  
  
  Momentum
&lt;/h2&gt;

&lt;p&gt;My adventures with buildpacks have been top of mind for a couple of weeks now. Oleg Šelajev and Cora Iberklied also presented about testcontainers in Tel Aviv. The stage was set for me to take this use case further.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Goal
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Build Spring Boot 3, native OCI images, with buildpacks, during demos, faster, with Testcontainers Cloud
&lt;/h3&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;You need to have an account at &lt;a href="https://testcontainers.cloud"&gt;https://testcontainers.cloud&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Login to your account
&lt;/h2&gt;

&lt;p&gt;I’m logging in with &lt;code&gt;v1.3.11&lt;/code&gt; of the cloud desktop app for Mac, this is still in private beta at the time of this writing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker context list

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;You should see at least one context named ’tcc'&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker context use tcc

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Create an application example
&lt;/h2&gt;

&lt;p&gt;Create a Spring Boot 3 application with &lt;code&gt;web&lt;/code&gt;, &lt;code&gt;actuator&lt;/code&gt;, and &lt;code&gt;testcontainers&lt;/code&gt; for a simple test.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl https://start.spring.io/starter.tgz -d dependencies=web,actuator,testcontainers -d javaVersion=17 -d bootVersion=3.0.0-RC2 -d type=maven-project | tar -xzf -

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Spring Boot 3 goes GA later this week, but I can’t wait that long&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In the pom.xml add this dependency:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;org.apache.maven.shared&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;maven-invoker&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;3.2.0&amp;lt;/version&amp;gt;
    &amp;lt;scope&amp;gt;test&amp;lt;/scope&amp;gt;
&amp;lt;/dependency&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add this property:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;testcontainers.version&amp;gt;1.17.6&amp;lt;/testcontainers.version&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add this dependency management section:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;dependencyManagement&amp;gt;
    &amp;lt;dependencies&amp;gt;
        &amp;lt;dependency&amp;gt;
            &amp;lt;groupId&amp;gt;org.testcontainers&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;testcontainers-bom&amp;lt;/artifactId&amp;gt;
            &amp;lt;version&amp;gt;${testcontainers.version}&amp;lt;/version&amp;gt;
            &amp;lt;type&amp;gt;pom&amp;lt;/type&amp;gt;
            &amp;lt;scope&amp;gt;import&amp;lt;/scope&amp;gt;
        &amp;lt;/dependency&amp;gt;
    &amp;lt;/dependencies&amp;gt;
&amp;lt;/dependencyManagement&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those pieces will allow you to write a test, that creates an OCI image, using buildpacks, with Testcontainers Cloud.&lt;/p&gt;

&lt;h2&gt;
  
  
  Write the test class
&lt;/h2&gt;

&lt;p&gt;Here is the test class that I’ve been reusing since June.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package com.example.demo;

import org.apache.maven.shared.invoker.DefaultInvocationRequest;
import org.apache.maven.shared.invoker.DefaultInvoker;
import org.apache.maven.shared.invoker.InvocationResult;
import org.apache.maven.shared.invoker.MavenInvocationException;
import org.junit.jupiter.api.Test;
import org.springframework.util.DigestUtils;
import org.testcontainers.DockerClientFactory;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.utility.LazyFuture;

import java.io.File;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.Future;

public class TccTest {
    private static final Future&amp;lt;String&amp;gt; IMAGE_FUTURE = new LazyFuture&amp;lt;&amp;gt;() {
        @Override
        protected String resolve() {
            // Find project's root dir
            File cwd;
            for (
                    cwd = new File(".");
                    !new File(cwd, "mvnw").isFile();
                    cwd = cwd.getParentFile()
            );

            // Make it unique per folder (for caching)
            var imageName = String.format(
                    "local/app-%s:%s",
                    DigestUtils.md5DigestAsHex(cwd.getAbsolutePath().getBytes()),
                    System.currentTimeMillis()
            );

            var properties = new Properties();
            properties.put("spring-boot.build-image.imageName", imageName);
            properties.put("skipTests", "true");

            var request = new DefaultInvocationRequest()
                    .addShellEnvironment("DOCKER_HOST", DockerClientFactory.instance().getTransportConfig().getDockerHost().toString())
                    .setPomFile(new File(cwd, "pom.xml"))
                    .setGoals(List.of("spring-boot:build-image"))
                    .setMavenExecutable(new File(cwd, "mvnw"))
                    .setProfiles(List.of("native"))
                    .setProperties(properties);

            InvocationResult invocationResult = null;
            try {
                invocationResult = new DefaultInvoker().execute(request);
            } catch (MavenInvocationException e) {
                throw new RuntimeException(e);
            }

            if (invocationResult.getExitCode() != 0) {
                throw new RuntimeException(invocationResult.getExecutionException());
            }

            return imageName;
        }
    };

    static final GenericContainer&amp;lt;?&amp;gt; APP = new GenericContainer&amp;lt;&amp;gt;(IMAGE_FUTURE)
            .withExposedPorts(8080);

    @Test
    void letsGo() throws Exception {
        APP.start();
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In &lt;a href="https://github.com/dashaun/spring-boot-3-buildpacks-with-testcontainers-cloud"&gt;my example repository&lt;/a&gt;, I actually create test classes for a regular OCI image and a &lt;code&gt;native&lt;/code&gt; OCI image.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verify
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;./mvnw clean test

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Boom! You get an OCI image, or two if you use &lt;a href="https://github.com/dashaun/spring-boot-3-buildpacks-with-testcontainers-cloud"&gt;my example repository&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker images | grep "local"

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Enjoy!&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;This is slick. It brings me joy. When I am presenting this topic, I no longer need to have &lt;code&gt;Docker Desktop&lt;/code&gt; running on my laptop. Therefore, my laptop battery will last longer. My builds will be consistently faster, using Testcontainers Cloud, and my demos will be smoother.&lt;/p&gt;

&lt;p&gt;I am completely rethinking a few of my own use cases. I will definitely create more Testcontainers Cloud content in the future.&lt;/p&gt;

&lt;p&gt;Please let me know what you think!&lt;/p&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://testcontainers.cloud"&gt;Testcontainers Cloud&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/dashaun/spring-boot-3-buildpacks-with-testcontainers-cloud"&gt;Example Repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://bsideup.github.io/posts/spring_boot_in_container/"&gt;Sergei Egorov Blog&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Vx9PlwbP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dashaun.com/posts/spring-boot-3-buildpacks-with-testcontainers-cloud/images/sergei-pairing.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Vx9PlwbP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dashaun.com/posts/spring-boot-3-buildpacks-with-testcontainers-cloud/images/sergei-pairing.png" alt="Pairing with Sergei" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>First attempt at a multi-arch buildpack for Spring Boot 3</title>
      <dc:creator>DaShaun</dc:creator>
      <pubDate>Sat, 12 Nov 2022 13:58:51 +0000</pubDate>
      <link>https://dev.to/dashaun/first-attempt-at-a-multi-arch-buildpack-for-spring-boot-3-4p2d</link>
      <guid>https://dev.to/dashaun/first-attempt-at-a-multi-arch-buildpack-for-spring-boot-3-4p2d</guid>
      <description>&lt;p&gt;My &lt;a href="https://dashaun.com/paketo-aarch64-builder-spring-boot-3.rc1"&gt;previous builder&lt;/a&gt; generated some fabulous feedback, including &lt;a href="https://github.com/jabrena/101-docker/issues/57"&gt;an issue in GitHub&lt;/a&gt;. It also opened the door for some wonderful collaborations while I have been traveling to Barcelona and Tel Aviv.&lt;/p&gt;

&lt;p&gt;On this journey of trying to get ARM64 support into the upstream &lt;a href="https://paketo.io"&gt;Paketo buildpacks&lt;/a&gt; I have learned so much. The next step is to prove that we can create the pipelines that can deliver a multi-architecture image.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Skip to the &lt;a href="https://github.com/dashaun/java-native-builder-multiarch"&gt;Quick Start here&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Docker manifest
&lt;/h2&gt;

&lt;p&gt;In order to see the architecture of an image, you can use the &lt;code&gt;docker manifest&lt;/code&gt; command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Inspect shows the image architecture
docker manifest inspect --verbose dashaun/java-native-builder-arm64:7.37.0

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Inspecting the ARM64 builder that was created in earlier article&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Create a new manifest list
&lt;/h2&gt;

&lt;p&gt;The process for slapping docker images together is pretty simple. Ideally you should be using the “same” image, but we are just proving a point here.&lt;/p&gt;

&lt;p&gt;My theory is that if I provide an &lt;code&gt;arm64&lt;/code&gt; architecture and an &lt;code&gt;amd64&lt;/code&gt; architecture to the same manifest list, that it will work as expected.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# create a new manifest with a name and tag
docker manifest create dashaun/java-native-builder-multiarch:7.37.0 \
# add ARM64 builder
--amend dashaun/java-native-builder-arm64:7.37.0 \
# add Paketo tiny builder for AMD64
--amend paketobuildpacks/builder:tiny

# push the manifest to the repository
docker manifest push dashaun/java-native-builder-multiarch:7.37.0

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;That’s it!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  inspect the manifest list - the builder
&lt;/h2&gt;

&lt;p&gt;Now you can inspect the manifest and see that it has packaged ARM64 and AMD64 architectures. This capability has been available for years. It is also surprisingly simple to accomplish as you can see.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker manifest inspect --verbose dashaun/java-native-builder-multiarch:7.37.0


[
    {
        "Ref": "docker.io/dashaun/java-native-builder-arm64:7.37.0",
        "Descriptor": {
            "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
            "digest": "sha256:80771c9c2efee25020ba60e320c41cc385a333434ad2982ed7326a0a413b9ae7",
            "size": 5150,
            "platform": {
                "architecture": "arm64",
                "os": "linux"
            }
        },
        "SchemaV2Manifest": {
            "schemaVersion": 2,
            "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
            "config": {
                "mediaType": "application/vnd.docker.container.image.v1+json",
                "size": 12587,
                "digest": "sha256:8cd1ead83689b853055ac2de0cc07c5f3c9d8d087b2ac97e838551fe8e962e00"
            },
            "layers": [
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 27195998,
                    "digest": "sha256:4e7e0215f4adc2c48ad9cb3b3781e21d474b477587f85682c2e2975ae91dce9d"
                },
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 4348,
                    "digest": "sha256:fc2e670f062f6edbb1b3be6aae88cbc0978a5273314c365bc4e99d861dfb5ff1"
                },
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 103281689,
                    "digest": "sha256:cba3892e6fcca4b47983803d53b777bc05476639fcfc1a2d4f384eac40363677"
                },
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 179,
                    "digest": "sha256:f5230b5f167cfc41fb9f96d53de0d9c5832e3b03e3a705052cf42ceada13aea9"
                },
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 197,
                    "digest": "sha256:357fefdf9bc907107a38600cf8d79c713346dc97370273d1aa79635d97a2f6f9"
                },
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 8870380,
                    "digest": "sha256:0ab7ccc1e1a42589b53d1397672009a979b296933efc276d79d3a2cdc336656c"
                },
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 2498256,
                    "digest": "sha256:fa161ac7a0015773c3f6890d6f1824957a6e21cee5a9bbf8ddc60e4e6d30a3ed"
                },
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 1806073,
                    "digest": "sha256:bdfdd20caee6a7044607d11d9a4f758a007ad2d61399ea5e3db22a7037ab4975"
                },
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 3273385,
                    "digest": "sha256:bc251569386a8e5fd513c85d9c51e76a258f0e59d0f945e185e101930012c9db"
                },
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 1509592,
                    "digest": "sha256:caeca36964d2da6c2b88abd998281e4fd5b708cfdd1440486f6acf7bc569292e"
                },
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 3191207,
                    "digest": "sha256:f840d9ee444653d04a2181d1d99e4f579d01b56349cce9325c6dade9af410998"
                },
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 3273637,
                    "digest": "sha256:b4f56cede28073295dc750e93678876a55b9fdafd97c940b97dc9a01ae345663"
                },
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 2387292,
                    "digest": "sha256:cebaf5f7c6e3350f5973f2c77f9ba90138d5a6bb2f1eb9e86084c8cf9f6117c6"
                },
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 4208510,
                    "digest": "sha256:47be0bf349071cb594970ef2d18f383b91231645ba49f27b5b39b47d9090aedc"
                },
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 1818619,
                    "digest": "sha256:5844db3d59f2bdc13aa19a6fd09789cf4a12aa87088759840a79c10eb01f93c5"
                },
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 1569855,
                    "digest": "sha256:89975d6b7daf9b8cf8b1a8350461e9e009b0ffc2a482becb9e1073adc3475153"
                },
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 2413551,
                    "digest": "sha256:aca3b8db4220fa7d69bf9cbd0bebc048cfa381174929828affb820c5b6b0f505"
                },
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 2480839,
                    "digest": "sha256:71bd4e702bfc97f1db60e6a5ac364897b2818dc2f92f1bf9d78c2935a1ae125d"
                },
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 2521983,
                    "digest": "sha256:3e1f506f69017dbcedb2a2b022ff0d6e63d4c0cd44e32fdcf6cc9e39b2416ade"
                },
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 2475996,
                    "digest": "sha256:f617bf8af0912672e16ed61332d6e7ad1f0ffc4fffe049da45ea670a1c441afd"
                },
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 362,
                    "digest": "sha256:0e3fabdd36281c4c1c946d09854510dc82ffd61476d62aaa474b3c64d8969ae3"
                },
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 151,
                    "digest": "sha256:9602e7831cd64261f173d4789e26f9cada67107ba1688409f7781bd737792b58"
                },
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 32,
                    "digest": "sha256:4f4fb700ef54461cfa02571ae0db9a0dc1e0cdb5577484a6d75e68dc38e8acc1"
                }
            ]
        }
    },
    {
        "Ref": "docker.io/paketobuildpacks/builder:tiny",
        "Descriptor": {
            "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
            "digest": "sha256:e5b4e4e720598e905baa793a94259ee75827949a6ba22a3d3196eb332e475dd6",
            "size": 9773,
            "platform": {
                "architecture": "amd64",
                "os": "linux"
            }
        },
        "SchemaV2Manifest": {
            "schemaVersion": 2,
            "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
            "config": {
                "mediaType": "application/vnd.docker.container.image.v1+json",
                "size": 64488,
                "digest": "sha256:94a3f804e111904682577130709e0cac6e0541e6b1931c1f9aa4f27326c974fc"
            },
            "layers": [
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 29589398,
                    "digest": "sha256:38496a9cab3e2bb43fa8ae1d45c3ddc1d2f89328e1f3973f029949b8cb2dd5fe"
                },
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 247,
                    "digest": "sha256:2e7826560210e2f0d4249f9ea20651fecd45306c2482767274c8c37289c23788"
                },
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 316,
                    "digest": "sha256:6013ff26a0374342ff4848b3171ddbe396413d80faa2b1ee67ca9afbfa64cafc"
                },
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 106179950,
                    "digest": "sha256:87196f2fd40c0ee5587ff771878aad41b42c1d29628c75fff9206b5c0b77f016"
                },
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 188,
                    "digest": "sha256:d61afba38b8e093dfc46b8e81c141b2d7eddc0903b0fc530b3af36a46a144af7"
                },
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 2744757,
                    "digest": "sha256:856e5ee7330534a520c3fd1d90260e9a5e99bfc182e0c2c3b18f7fea06f0bb0e"
                },
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 807,
                    "digest": "sha256:7eededf5887404e40dfc0d1d533fb9db3fe6b725213e4f3edda1ca1f5b1492c4"
                },
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 197,
                    "digest": "sha256:357fefdf9bc907107a38600cf8d79c713346dc97370273d1aa79635d97a2f6f9"
                },
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 9753557,
                    "digest": "sha256:d0d113e4e2bd21dbaa2f66e687ec4a23e4e83331b7845b85d0c10de1da05b08d"
                },
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 2574003,
                    "digest": "sha256:41869a3aefd1a5bf8db003eb401d1af5ef2568c92262c3038f553d38145f36a1"
                },
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 2573999,
                    "digest": "sha256:053ee41df41d6484ffec64fda64937a6418a1404f602487019fccddbad2958de"
                },
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 4641532,
                    "digest": "sha256:0fec499a25de5a35aa814bed46943a93c94d0e6785eedc679874ec3da04bc748"
                },
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 2525673,
                    "digest": "sha256:c7b3308e13d3f30bde70d2293590aefdb95e52784b8bb840e13c587d354604e6"
                },
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 8714351,
                    "digest": "sha256:51918c870f73c8ceffc527e79041e08d3101beeabd0c0bc44555b6faf1188cdd"
                },
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 3197061,
                    "digest": "sha256:fbb9179cbbb1aa191e9ec19e77b7e6226b97ba8ac9d382822970ddcc61ce05e2"
                },
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 2661004,
                    "digest": "sha256:38876f988da32a7ef093d5245f6923bb9b748362834345a3e8995f72ef49e168"
                },
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 2547018,
                    "digest": "sha256:b847bb2fe3fdc6bd69cd7d690bcef5a1399310ce17bea49bbd1d5fd3641d0140"
                },
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 3291941,
                    "digest": "sha256:78b11fcb7ca30ba09defc8dcfc1298ea20b970430d9836b4bc7c404840b49a03"
                },
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 3255182,
                    "digest": "sha256:18ee4cd834fc95727859c15eaedc1f5670c4e9f3f74e3f45a108d5b9167344ab"
                },
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 1576728,
                    "digest": "sha256:ad443f24b1ed77880e534a0c1aca0ca5a722e866b7611a85e9fd7ef3463dea08"
                },
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 1511595,
                    "digest": "sha256:a6cd17527f03923a723839bdbe57ec200a008de6c8b35c0c4d900a675adac014"
                },
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 2689335,
                    "digest": "sha256:cd146e648d04aaf6161526eb8be96d3bf9c8fc22e433c4e6834dc972b4f09830"
                },
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 3478686,
                    "digest": "sha256:bf32d3cbc5fb2d1c54c54f964b11e10d70c58f340dc17857d45db246807bcca6"
                },
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 2639800,
                    "digest": "sha256:c505716a4281aec3fc577e417a29d23a19d1ff720f498aef49b9ed362ab53d88"
                },
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 3382413,
                    "digest": "sha256:17c8e16edff8afd8ef3b3b1d1d061351245287c59c6ab8846c1af94b414e1865"
                },
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 555,
                    "digest": "sha256:57bf259ba83920a4e2575582423c084670209a37fdb503fbbc7e10d75a5419d0"
                },
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 2798929,
                    "digest": "sha256:e71af6bec187b3f6627f52f6f2808c6b946b5cc09b7f4a6ee006cc5557254ebc"
                },
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 1678930,
                    "digest": "sha256:fcf29a1baad8cb3d657d685b47ab630602a5c47b333f7ed9d7cbcae68a300801"
                },
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 1826608,
                    "digest": "sha256:fad10dc586613d133e71639507d157251d8d533d9c94c3897ed5ae6c557bcdf7"
                },
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 2637303,
                    "digest": "sha256:bc22ffea56cce18044465269f6958bfb8ef37e182c82c6ab9f2821525c034717"
                },
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 1623998,
                    "digest": "sha256:63fd16f5b2cc4af356f18b61281870994919fcb17f3a51f490fc09b6cd5e0cfe"
                },
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 3091878,
                    "digest": "sha256:71dea8ea27aab08e92be2cb1c61ba12693f8dc622575cd9087e689fdabd048fd"
                },
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 8496432,
                    "digest": "sha256:547d0874d2b5a13d766fc98a54725cce8b5ec50af624d5c21b971ca0e668d7e0"
                },
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 2644647,
                    "digest": "sha256:27bd211db33f03f1ad558807c8312a3bf537e154b591f7067a7eab8d9364f10b"
                },
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 5318,
                    "digest": "sha256:3455451f8d3fd909057c5f94d9c34b5618fa6f62db51ed540cd7931c146a5fd6"
                },
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 3472177,
                    "digest": "sha256:a77b05080df7a983ee586813839673e2dfdb852c87589ddf0900517bc928b779"
                },
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 8659224,
                    "digest": "sha256:6a58fb7cc75d73d08a6d85d995ad767e5693c4afd8b4f94f89f61018a3925c67"
                },
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 5136,
                    "digest": "sha256:437d9ac90cf3a6df605fbc9b691455242c3f4d7a3ba89b683daa5885314f1909"
                },
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 4385434,
                    "digest": "sha256:52814f0ca0c9d85074d817b558ccb3583cdb394d1ec02cf3e45e860b24ef3f7b"
                },
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 1838119,
                    "digest": "sha256:7e8facf10ccafea1b9688a4a4ba424d36f92e3e4cea1e913577bb45eb5bdc5f9"
                },
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 2464280,
                    "digest": "sha256:b8531cb5032999225be8589ab8bec5ea275fffb47353863c29d1afcdf7966104"
                },
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 3468583,
                    "digest": "sha256:55e985d488da8faeaa9772b7cfb3073d458fa626c89a390b292ab879be30f157"
                },
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 214,
                    "digest": "sha256:7d37e76963e86dbc76a9cbd3a6afa21c127bb790f77415b608da57daa9019f10"
                },
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 187,
                    "digest": "sha256:3aaaa9c943efae7533b9a28a28a676354866cad58208bcd7988b055c754653f0"
                },
                {
                    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
                    "size": 32,
                    "digest": "sha256:4f4fb700ef54461cfa02571ae0db9a0dc1e0cdb5577484a6d75e68dc38e8acc1"
                }
            ]
        }
    }
]

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  validation
&lt;/h2&gt;

&lt;p&gt;Create a Spring Boot 3 application with &lt;code&gt;web&lt;/code&gt; and &lt;code&gt;actuator&lt;/code&gt; which is enough for a simple test.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Create a Spring Boot 3 application
curl https://start.spring.io/starter.tgz -d dependencies=web,actuator -d javaVersion=17 -d bootVersion=3.0.0-SNAPSHOT -d type=maven-project | tar -xzf -

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;I’m using SNAPSHOT, because YOLO, but RC2 is already available!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Add a profile to the pom.xml, that will use the &lt;code&gt;multi-arch&lt;/code&gt; builder that we created above.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Remove the last line of the pom.xml file (on Mac OS X)
sed -i '' -e '$ d' pom.xml
# Add the new profile to the end of the pom.xml
echo "
  &amp;lt;profiles&amp;gt;
    &amp;lt;profile&amp;gt;
      &amp;lt;id&amp;gt;dashaun&amp;lt;/id&amp;gt;
      &amp;lt;build&amp;gt;
        &amp;lt;plugins&amp;gt;
          &amp;lt;plugin&amp;gt;
            &amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;spring-boot-maven-plugin&amp;lt;/artifactId&amp;gt;
            &amp;lt;configuration&amp;gt;
              &amp;lt;image&amp;gt;
                &amp;lt;builder&amp;gt;dashaun/java-native-builder-multiarch:7.37.0&amp;lt;/builder&amp;gt;
              &amp;lt;/image&amp;gt;
            &amp;lt;/configuration&amp;gt;
          &amp;lt;/plugin&amp;gt;
        &amp;lt;/plugins&amp;gt;
      &amp;lt;/build&amp;gt;
    &amp;lt;/profile&amp;gt;
  &amp;lt;/profiles&amp;gt;
&amp;lt;/project&amp;gt;" &amp;gt;&amp;gt; pom.xml 

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now your project will use the multi-architecture builder that created.&lt;/p&gt;

&lt;h2&gt;
  
  
  optional
&lt;/h2&gt;

&lt;p&gt;To watch, and be amazed, by the correct images being pulled down to your machine, run these commands to remove any unused images from your machine.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker kill $(docker ps -q)
docker system prune -a --volumes

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  build on AMD64 or ARM64
&lt;/h2&gt;

&lt;p&gt;The magical moment! You can do this from AMD64 or ARM64.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;./mvnw -Pnative,dashaun spring-boot:build-image

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should have an image created. One layer of that image, is a statically linked binary for your architecture.&lt;/p&gt;

&lt;h2&gt;
  
  
  inspect the new image.
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# find the local image
docker images | grep demo
# Look at the architecture for the image
docker inspect demo:0.0.1-SNAPSHOT | jq '.[0].Architecture'

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;The &lt;code&gt;| jq.[0].Architecture&lt;/code&gt; can be replaced with &lt;code&gt;| grep Architecture&lt;/code&gt; if you don’t have &lt;code&gt;jq&lt;/code&gt; installed&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  run and test the image
&lt;/h2&gt;

&lt;p&gt;Run the OCI image with &lt;code&gt;docker&lt;/code&gt; to start up the server. It should start quickly!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Forward the port, run in the background, but see the startup time
docker run -p 8080:8080 demo:0.0.1-SNAPSHOT 
# Check the endpoint to validate
http :8080/actuator/health

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/dashaun/java-native-builder-multiarch"&gt;GitHub dashaun/java-native-builder-multiarch&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hub.docker.com/r/dashaun/java-native-builder-multiarch"&gt;Docker dashaun/java-native-builder-multiarch&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/dashaun/paketo-arm64"&gt;dashaun/paketo-arm64&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dashaun.com/posts/paketo-aarch64-builder-spring-boot-3-rc1/"&gt;Related Post&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Thanks
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://twitter.com/dashaun/status/1591432189488803840"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--M6az9bBH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dashaun.com/posts/multiarch-builder-poc/images/itsworking.png" alt="Its working" width="800" height="978"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Finally
&lt;/h2&gt;

&lt;p&gt;I want to hear from you! All of my social links are &lt;a href="https://dashaun.com"&gt;here at dashaun.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Issues and feedback can be left in the &lt;a href="https://github.com/dashaun/java-native-builder-multiarch"&gt;GitHub dashaun/java-native-builder-multiarch&lt;/a&gt; repository.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>A new builder for Spring Boot 3 RC1 on ARM64</title>
      <dc:creator>DaShaun</dc:creator>
      <pubDate>Thu, 10 Nov 2022 11:18:05 +0000</pubDate>
      <link>https://dev.to/dashaun/a-new-builder-for-spring-boot-3-rc1-on-arm64-4k31</link>
      <guid>https://dev.to/dashaun/a-new-builder-for-spring-boot-3-rc1-on-arm64-4k31</guid>
      <description>&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;I’m a fan of using buildpacks. I’ve been &lt;a href="https://dashaun.com/posts/springboot-arm64-image/"&gt;using&lt;/a&gt; and updating the &lt;a href="https://github.com/dmikusa/paketo-arm64"&gt;paketo-arm64 repository started by Daniel Mikusa&lt;/a&gt;. With Spring Boot 3 going GA, very soon, more people are wanting to use buildpacks with their M1 machines. I feel pressure, in a great way, to get a new version released that includes the latest version of GraalVM.&lt;/p&gt;

&lt;p&gt;Because I’m not doing this regularly, I forget the steps that I take to create a new build. So this post, is really a reminder-to-self, but hopefully it helps someone else also.&lt;/p&gt;

&lt;p&gt;You can follow along with this on &lt;code&gt;M1&lt;/code&gt;,&lt;code&gt;Raspberry Pi&lt;/code&gt;, or a free-tier &lt;code&gt;ARM64&lt;/code&gt; instance from Oracle.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisite
&lt;/h2&gt;

&lt;p&gt;I’ve been setting up a temporary ARM64 machine on Oracle Cloud for ARM64/aarch64 builds. The repository I use is &lt;a href="https://github.com/dashaun/oci-arm64-gha-runner-terraform"&gt;here&lt;/a&gt;. I should probably talk about that in another post, also.&lt;/p&gt;

&lt;p&gt;My first step was to deploy that machine, so it can be used to build the builder! Once I got the machine up and connected to my repository, I was ready to update the code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make changes
&lt;/h2&gt;

&lt;p&gt;First I just made changes to the versions in &lt;a href="https://raw.githubusercontent.com/dashaun/paketo-arm64/main/.github/workflows/paketo-arm64.yml"&gt;my GitHub workflow&lt;/a&gt;. That worked and pushed out a new image, but it wasn’t enough.&lt;/p&gt;

&lt;p&gt;I had to re-read the workflow to understand what I was missing.&lt;/p&gt;

&lt;p&gt;For each of the files in the &lt;code&gt;arm64-toml&lt;/code&gt; of &lt;a href="https://github.com/dashaun/paketo-arm64"&gt;the repository&lt;/a&gt; I needed to update to the latest versions. This is a tedious, and currently manual, process that requires copy, paste, merge and then creating sha256 values.&lt;/p&gt;

&lt;p&gt;For these versions, it was extra messy, because naming conventions changed since my last release. In some places &lt;code&gt;arm64&lt;/code&gt; was replaced with &lt;code&gt;aarch64&lt;/code&gt; as one example.&lt;/p&gt;

&lt;p&gt;Additionally, some dependencies had even more releases, and I decided to go with the &lt;code&gt;full&lt;/code&gt; option when it was available.&lt;/p&gt;

&lt;p&gt;The last real time-suck was the &lt;code&gt;sha&lt;/code&gt; values. In the GitHub releases, the sha values are provided as &lt;code&gt;sha1sum&lt;/code&gt; values. In the builder metadata, it requires &lt;code&gt;sha256&lt;/code&gt; values. Since I couldn’t find &lt;code&gt;sha256&lt;/code&gt; values published anywhere, I had to download the dependencies and calculate the values manually. For each dependency update, &lt;code&gt;curl&lt;/code&gt; then &lt;code&gt;shasum&lt;/code&gt;, which can be error prone.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -L https://github.com/bell-sw/Liberica/releases/download/8u352+8/bellsoft-jdk8u352+8-linux-aarch64.tar.gz -O
shasum -a 256 bellsoft-jdk8u352+8-linux-aarch64.tar.gz

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hopefully, this post and the small updates I made to the &lt;code&gt;README.md&lt;/code&gt; will remind me next time. I should probably figure out a way to automate some of that. Maybe a CLI?&lt;/p&gt;

&lt;h2&gt;
  
  
  Create an application image
&lt;/h2&gt;

&lt;p&gt;Create a Spring Boot 3 application with &lt;code&gt;web&lt;/code&gt; and &lt;code&gt;actuator&lt;/code&gt; which is enough for a simple test.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Create a Spring Boot 3 application
curl https://start.spring.io/starter.tgz -d dependencies=web,actuator -d javaVersion=17 -d bootVersion=3.0.0-SNAPSHOT -d type=maven-project | tar -xzf -

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the pom.xml replace this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;build&amp;gt;
    &amp;lt;plugins&amp;gt;
        &amp;lt;plugin&amp;gt;
            &amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;spring-boot-maven-plugin&amp;lt;/artifactId&amp;gt;
        &amp;lt;/plugin&amp;gt;
    &amp;lt;/plugins&amp;gt;
&amp;lt;/build&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;with this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;build&amp;gt;
    &amp;lt;plugins&amp;gt;
        &amp;lt;plugin&amp;gt;
            &amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;spring-boot-maven-plugin&amp;lt;/artifactId&amp;gt;
            &amp;lt;configuration&amp;gt;
                &amp;lt;image&amp;gt;
                    &amp;lt;builder&amp;gt;dashaun/java-native-builder-arm64:7.37.0&amp;lt;/builder&amp;gt;
                &amp;lt;/image&amp;gt;
            &amp;lt;/configuration&amp;gt;
        &amp;lt;/plugin&amp;gt;
    &amp;lt;/plugins&amp;gt;
&amp;lt;/build&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Build an image using the image builder that we have created and configured.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;./mvnw -Pnative spring-boot:build-image

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Run and test the image
&lt;/h2&gt;

&lt;p&gt;Run the OCI image with &lt;code&gt;docker&lt;/code&gt; to start up the server. It should start quickly!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Forward the part, run in the background, but see the startup time
docker run -p 8080:8080 demo:0.0.1-SNAPSHOT &amp;amp;
# Check the endpoint to validate
http http :8080/actuator/health

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Finally
&lt;/h2&gt;

&lt;p&gt;Because I’m done, and moved &lt;a href="https://hub.docker.com/r/dashaun/java-native-builder-arm64"&gt;builder version 7.37.0 to prod&lt;/a&gt;, I also cleaned up my &lt;code&gt;terraform&lt;/code&gt; deployment.&lt;/p&gt;

&lt;p&gt;Another huge “Thanks” to &lt;a href="https://twitter.com/dmikusa"&gt;Daniel Mikusa&lt;/a&gt; for all of his help.&lt;/p&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://hub.docker.com/r/dashaun/java-native-builder-arm64"&gt;dashaun/java-native-builder-arm64&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/dashaun/paketo-arm64"&gt;dashaun/paketo-arm64&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/dashaun/oci-arm64-gha-runner-terraform"&gt;dashaun/oci-arm64-gha-runner-terraform&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dashaun.com/posts/springboot-arm64-image/"&gt;Related Post&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>springboot</category>
      <category>buildpack</category>
      <category>arm64</category>
      <category>m1</category>
    </item>
    <item>
      <title>Spring Boot StackPath Terraform</title>
      <dc:creator>DaShaun</dc:creator>
      <pubDate>Tue, 25 Oct 2022 16:19:03 +0000</pubDate>
      <link>https://dev.to/dashaun/spring-boot-stackpath-terraform-2hak</link>
      <guid>https://dev.to/dashaun/spring-boot-stackpath-terraform-2hak</guid>
      <description>&lt;p&gt;This is another article based on recent &lt;a href="https://bit.ly/spring-office-hours"&gt;Spring Office Hours&lt;/a&gt; sessions. Spring Boot provides several effective ways to deliver applications, to production, efficiently. There are many situations where one of the &lt;code&gt;hyperscale&lt;/code&gt; cloud providers is not an option, or the best choice.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dashaun.com/posts/spring-boot-kamatera-terraform/"&gt;Previously, I looked at Kamatera.&lt;/a&gt;This time, I’m going to use &lt;a href="https://stackpath.com"&gt;StackPath&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://youtu.be/kWVv0U1Bvq0"&gt;I did the work during a live stream, so you can see exactly how easy and exciting this was.&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Initial Thoughts
&lt;/h2&gt;

&lt;p&gt;I figured that going down this path would get easier with each provider. So far that has been true. I’m learning more about Terraform and getting more comfortable. I’m able to map the different concepts and building blocks a little easier with each rep.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting an account
&lt;/h2&gt;

&lt;p&gt;As you would expect, nothing out of the ordinary when trying to setup an account. I couldn’t find a free tier or discount code, but I didn’t look very hard.&lt;a href="https://dashaun.com"&gt;Reach out to me&lt;/a&gt; if you know of something that I can share. Billing info is required.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stackpath Terraform Provider
&lt;/h2&gt;

&lt;p&gt;Having a Terraform Provider is what I have set as the bare minimum for this exercise. The &lt;a href="https://registry.terraform.io/providers/stackpath/stackpath/latest/docs"&gt;StackPath Provider&lt;/a&gt; was surprising. I consider myself a slightly above average Terraform user. I have never experienced this as the &lt;code&gt;default&lt;/code&gt; behavior for a &lt;code&gt;Provider&lt;/code&gt; and I am impressed.&lt;/p&gt;

&lt;p&gt;The provider only required three arguments for configuration. I decided to use the environment variable approach rather than passing in variables.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;STACKPATH_STACK_ID
STACKPATH_CLIENT_ID
STACKPATH_CLIENT_SECRET

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;For the &lt;code&gt;STACKPATH_STACK_ID&lt;/code&gt; I used the &lt;code&gt;stack slug&lt;/code&gt; from the &lt;code&gt;DASHBOARD&lt;/code&gt;. I created the &lt;code&gt;STACKPATH_CLIENT_ID&lt;/code&gt; and &lt;code&gt;STACKPATH_CLIENT_SECRET&lt;/code&gt; from the &lt;code&gt;API ACCESS&lt;/code&gt; option of the dashboard.&lt;/p&gt;

&lt;p&gt;When I ran the &lt;code&gt;terraform plan&lt;/code&gt; and &lt;code&gt;terraform apply&lt;/code&gt; they were so fast I thought it was broken. I had guessed at the value to use for &lt;code&gt;STACKPATH_STACK_ID&lt;/code&gt; and figured that I must be doing something wrong. Nope. Their platform API defaults to an &lt;code&gt;async&lt;/code&gt; behavior that doesn’t wait for object creation to be completed. Instead, your actions complete, and you can run &lt;code&gt;terraform refresh&lt;/code&gt; to get the status of its creation.&lt;/p&gt;

&lt;p&gt;I’ve used other &lt;code&gt;Providers&lt;/code&gt; that provide this as an option. In this case, while I was learning and iterating frequently, it felt amazing.&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
      &lt;div class="c-embed__cover"&gt;
        &lt;a href="https://asciinema.org/a/JqexvsUYphyGVpSuEOADpfbnM?autoplay=1&amp;amp;amp%3BstartAt=03" class="c-link s:max-w-50 align-middle" rel="noopener noreferrer"&gt;
          &lt;img alt="" src="https://res.cloudinary.com/practicaldev/image/fetch/s--jywtYaQQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://asciinema.org/a/JqexvsUYphyGVpSuEOADpfbnM.png" height="181" class="m-0" width="800"&gt;
        &lt;/a&gt;
      &lt;/div&gt;
    &lt;div class="c-embed__body"&gt;
      &lt;h2 class="fs-xl lh-tight"&gt;
        &lt;a href="https://asciinema.org/a/JqexvsUYphyGVpSuEOADpfbnM?autoplay=1&amp;amp;amp%3BstartAt=03" rel="noopener noreferrer" class="c-link"&gt;
          Terraform Init, Terraform Apply via StackPath Provider - asciinema.org
        &lt;/a&gt;
      &lt;/h2&gt;
      &lt;div class="color-secondary fs-s flex items-center"&gt;
          &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://res.cloudinary.com/practicaldev/image/fetch/s--XuZRMMKY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://asciinema.org/images/favicon-2d62dafa447cf018340b7121007568e3.png%3Fvsn%3Dd" width="32" height="32"&gt;
        asciinema.org
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;



&lt;p&gt;To check on the status I used &lt;code&gt;terraform refresh&lt;/code&gt; and in this mode, it felt like the right way to do things.&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
      &lt;div class="c-embed__cover"&gt;
        &lt;a href="https://asciinema.org/a/jUlDDHoeox4SXBpfFHzPPPD37?autoplay=1" class="c-link s:max-w-50 align-middle" rel="noopener noreferrer"&gt;
          &lt;img alt="" src="https://res.cloudinary.com/practicaldev/image/fetch/s--PFo8p0j4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://asciinema.org/a/jUlDDHoeox4SXBpfFHzPPPD37.png" height="181" class="m-0" width="800"&gt;
        &lt;/a&gt;
      &lt;/div&gt;
    &lt;div class="c-embed__body"&gt;
      &lt;h2 class="fs-xl lh-tight"&gt;
        &lt;a href="https://asciinema.org/a/jUlDDHoeox4SXBpfFHzPPPD37?autoplay=1" rel="noopener noreferrer" class="c-link"&gt;
          Terraform Refresh via StackPath Provider - asciinema.org
        &lt;/a&gt;
      &lt;/h2&gt;
      &lt;div class="color-secondary fs-s flex items-center"&gt;
          &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://res.cloudinary.com/practicaldev/image/fetch/s--XuZRMMKY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://asciinema.org/images/favicon-2d62dafa447cf018340b7121007568e3.png%3Fvsn%3Dd" width="32" height="32"&gt;
        asciinema.org
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Accessing the service felt snappy.&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
      &lt;div class="c-embed__cover"&gt;
        &lt;a href="https://asciinema.org/a/LHmJDSfJXSG9WTRDOXoYtDBc9?autoplay=1&amp;amp;amp%3BstartAt=2" class="c-link s:max-w-50 align-middle" rel="noopener noreferrer"&gt;
          &lt;img alt="" src="https://res.cloudinary.com/practicaldev/image/fetch/s--Yw3lkH2z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://asciinema.org/a/LHmJDSfJXSG9WTRDOXoYtDBc9.png" height="181" class="m-0" width="800"&gt;
        &lt;/a&gt;
      &lt;/div&gt;
    &lt;div class="c-embed__body"&gt;
      &lt;h2 class="fs-xl lh-tight"&gt;
        &lt;a href="https://asciinema.org/a/LHmJDSfJXSG9WTRDOXoYtDBc9?autoplay=1&amp;amp;amp%3BstartAt=2" rel="noopener noreferrer" class="c-link"&gt;
          Using Curl to Validate Workload deployed to StackPath - asciinema.org
        &lt;/a&gt;
      &lt;/h2&gt;
      &lt;div class="color-secondary fs-s flex items-center"&gt;
          &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://res.cloudinary.com/practicaldev/image/fetch/s--XuZRMMKY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://asciinema.org/images/favicon-2d62dafa447cf018340b7121007568e3.png%3Fvsn%3Dd" width="32" height="32"&gt;
        asciinema.org
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Cleaning up, was just as fast, so iterating through the struggles was relatively painless.&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
      &lt;div class="c-embed__cover"&gt;
        &lt;a href="https://asciinema.org/a/dK1ARCkLHZNp2UQaPozfFgNo0" class="c-link s:max-w-50 align-middle" rel="noopener noreferrer"&gt;
          &lt;img alt="" src="https://res.cloudinary.com/practicaldev/image/fetch/s--ugYM1h3I--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://asciinema.org/a/dK1ARCkLHZNp2UQaPozfFgNo0.png" height="181" class="m-0" width="800"&gt;
        &lt;/a&gt;
      &lt;/div&gt;
    &lt;div class="c-embed__body"&gt;
      &lt;h2 class="fs-xl lh-tight"&gt;
        &lt;a href="https://asciinema.org/a/dK1ARCkLHZNp2UQaPozfFgNo0" rel="noopener noreferrer" class="c-link"&gt;
          Terraform Destroy via StackPath Provider - asciinema.org
        &lt;/a&gt;
      &lt;/h2&gt;
      &lt;div class="color-secondary fs-s flex items-center"&gt;
          &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://res.cloudinary.com/practicaldev/image/fetch/s--XuZRMMKY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://asciinema.org/images/favicon-2d62dafa447cf018340b7121007568e3.png%3Fvsn%3Dd" width="32" height="32"&gt;
        asciinema.org
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  Struggle
&lt;/h2&gt;

&lt;p&gt;I spent a bunch of time not realizing that I needed the &lt;code&gt;stackpath_compute_network_policy&lt;/code&gt; to allow public traffic. On this platform, ports are not exposed to the public by default. Even though I struggled, this made me feel a little more secure. Simply because I am forced to be specific about what I’m allowing. Additionally, the container doesn’t have remote access enabled by default.&lt;/p&gt;

&lt;h2&gt;
  
  
  Differences
&lt;/h2&gt;

&lt;p&gt;Deploying a VM that can run Docker is one thing. Deploying a &lt;code&gt;container&lt;/code&gt; to a &lt;code&gt;platform&lt;/code&gt; is a different pattern. While I was wondering through the documentation, I was very interested. There are some very advanced and mature capabilities available. This is clearly a platform that is designed to support short-lived and very dynamic workloads.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;I only scratched the service here, but it worked, and it worked quickly. There are some very interesting architectures that can be accomplished with &lt;code&gt;StackPath&lt;/code&gt; and I’m excited to go further. I didn’t know what to expect on this journey. I can say that &lt;code&gt;StackPath&lt;/code&gt; his raised the bar for my expectations going forward.&lt;/p&gt;

&lt;h2&gt;
  
  
  Your Turn
&lt;/h2&gt;

&lt;p&gt;Take the &lt;a href="https://github.com/dashaun/spring-boot-stackpath-terraform"&gt;repository&lt;/a&gt; for a spin and try it out for yourself. Let me know what you think.&lt;/p&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/dashaun/spring-boot-stackpath-terraform"&gt;https://github.com/dashaun/spring-boot-stackpath-terraform&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://youtu.be/kWVv0U1Bvq0"&gt;YouTube Video&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitch.tv/javagrunt"&gt;Javagrunt on Twitch&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
  </channel>
</rss>
