<?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: buildbasekit</title>
    <description>The latest articles on DEV Community by buildbasekit (@buildbasekit).</description>
    <link>https://dev.to/buildbasekit</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3822341%2Fd883b38f-434e-4524-aa08-2a8372503386.webp</url>
      <title>DEV Community: buildbasekit</title>
      <link>https://dev.to/buildbasekit</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/buildbasekit"/>
    <language>en</language>
    <item>
      <title>JWT vs Session Authentication in Spring Boot: Which One Should You Use?</title>
      <dc:creator>buildbasekit</dc:creator>
      <pubDate>Thu, 09 Apr 2026 12:07:25 +0000</pubDate>
      <link>https://dev.to/buildbasekit/jwt-vs-session-authentication-in-spring-boot-which-one-should-you-use-4gj5</link>
      <guid>https://dev.to/buildbasekit/jwt-vs-session-authentication-in-spring-boot-which-one-should-you-use-4gj5</guid>
      <description>&lt;p&gt;Most developers pick JWT or sessions based on tutorials or trends.&lt;/p&gt;

&lt;p&gt;That is a mistake.&lt;/p&gt;

&lt;p&gt;The wrong choice can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;break scalability
&lt;/li&gt;
&lt;li&gt;increase complexity
&lt;/li&gt;
&lt;li&gt;create security issues later
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This guide explains how both actually work in real systems and how to choose the right one.&lt;/p&gt;




&lt;h2&gt;
  
  
  Quick Answer
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;strong&gt;JWT&lt;/strong&gt; for APIs, microservices, and scalable systems
&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;sessions&lt;/strong&gt; for simple server rendered applications
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;JWT is stateless and scalable&lt;br&gt;&lt;br&gt;
Sessions are simpler but harder to scale  &lt;/p&gt;




&lt;h2&gt;
  
  
  When This Decision Matters
&lt;/h2&gt;

&lt;p&gt;You need to choose carefully if you are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;building REST APIs
&lt;/li&gt;
&lt;li&gt;creating login systems
&lt;/li&gt;
&lt;li&gt;scaling across multiple servers
&lt;/li&gt;
&lt;li&gt;deciding between stateless vs stateful architecture
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Choosing wrong early creates pain later.&lt;/p&gt;




&lt;h2&gt;
  
  
  What is Session Based Authentication
&lt;/h2&gt;

&lt;p&gt;Session authentication stores user state on the server.&lt;/p&gt;

&lt;h3&gt;
  
  
  How it works:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;server creates a session after login
&lt;/li&gt;
&lt;li&gt;session data is stored on server
&lt;/li&gt;
&lt;li&gt;client sends session ID with each request
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Key characteristics:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;stateful
&lt;/li&gt;
&lt;li&gt;simple to implement
&lt;/li&gt;
&lt;li&gt;tightly coupled to server
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Problem:
&lt;/h3&gt;

&lt;p&gt;Scaling requires shared storage like Redis.&lt;/p&gt;




&lt;h2&gt;
  
  
  What is JWT Authentication
&lt;/h2&gt;

&lt;p&gt;JWT is a stateless authentication method.&lt;/p&gt;

&lt;h3&gt;
  
  
  How it works:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;server generates a token
&lt;/li&gt;
&lt;li&gt;client stores token
&lt;/li&gt;
&lt;li&gt;token is sent with every request
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Key characteristics:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;stateless
&lt;/li&gt;
&lt;li&gt;no server side storage
&lt;/li&gt;
&lt;li&gt;works well across services
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Tradeoff:
&lt;/h3&gt;

&lt;p&gt;Token expiration and revocation are harder.&lt;/p&gt;




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

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Session&lt;/th&gt;
&lt;th&gt;JWT&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;State&lt;/td&gt;
&lt;td&gt;Stateful&lt;/td&gt;
&lt;td&gt;Stateless&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Storage&lt;/td&gt;
&lt;td&gt;Server&lt;/td&gt;
&lt;td&gt;Client&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scalability&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best For&lt;/td&gt;
&lt;td&gt;Monolith apps&lt;/td&gt;
&lt;td&gt;APIs and microservices&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  When to Use Session Authentication
&lt;/h2&gt;

&lt;p&gt;Use sessions if:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you are building server rendered apps
&lt;/li&gt;
&lt;li&gt;your system is small or medium scale
&lt;/li&gt;
&lt;li&gt;you want simple session invalidation
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sessions are easier to manage but not built for scale.&lt;/p&gt;




&lt;h2&gt;
  
  
  When to Use JWT Authentication
&lt;/h2&gt;

&lt;p&gt;Use JWT if:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you are building REST APIs
&lt;/li&gt;
&lt;li&gt;frontend and backend are separate
&lt;/li&gt;
&lt;li&gt;you need scalability
&lt;/li&gt;
&lt;li&gt;you are using microservices
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;JWT fits modern backend architecture better.&lt;/p&gt;




&lt;h2&gt;
  
  
  Authentication Flow
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Session Flow
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;user logs in
&lt;/li&gt;
&lt;li&gt;server creates session
&lt;/li&gt;
&lt;li&gt;session ID stored in cookie
&lt;/li&gt;
&lt;li&gt;server validates session on every request
&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  JWT Flow
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;user logs in
&lt;/li&gt;
&lt;li&gt;server generates token
&lt;/li&gt;
&lt;li&gt;client stores token
&lt;/li&gt;
&lt;li&gt;token sent with each request
&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Example: JWT Filter in Spring Boot
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;JwtFilter&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;OncePerRequestFilter&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;protected&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;doFilterInternal&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;HttpServletRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                                    &lt;span class="nc"&gt;HttpServletResponse&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                                    &lt;span class="nc"&gt;FilterChain&lt;/span&gt; &lt;span class="n"&gt;chain&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// extract token&lt;/span&gt;
        &lt;span class="c1"&gt;// validate token&lt;/span&gt;
        &lt;span class="c1"&gt;// set authentication&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;h2&gt;
  
  
  Common Mistakes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;using JWT without handling token expiration&lt;/li&gt;
&lt;li&gt;using sessions in distributed systems without shared storage&lt;/li&gt;
&lt;li&gt;choosing based on trends instead of requirements&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These mistakes cause issues in production.&lt;/p&gt;




&lt;h2&gt;
  
  
  Related Guides
&lt;/h2&gt;

&lt;p&gt;If you want implementation details:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://buildbasekit.com/blogs/spring-boot-jwt-authentication/" rel="noopener noreferrer"&gt;https://buildbasekit.com/blogs/spring-boot-jwt-authentication/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For real backend architecture:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://buildbasekit.com/blogs/spring-boot-file-upload-production-guide/" rel="noopener noreferrer"&gt;https://buildbasekit.com/blogs/spring-boot-file-upload-production-guide/&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;There is no universal best option.&lt;/p&gt;

&lt;p&gt;JWT is better for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;APIs&lt;/li&gt;
&lt;li&gt;distributed systems&lt;/li&gt;
&lt;li&gt;scalable backends&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sessions are better for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;simple apps&lt;/li&gt;
&lt;li&gt;quick setups&lt;/li&gt;
&lt;li&gt;server rendered systems&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Choose based on your architecture, not trends.&lt;/p&gt;




&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Is JWT more secure than sessions?
&lt;/h3&gt;

&lt;p&gt;No. Security depends on implementation, not the method.&lt;/p&gt;




&lt;h3&gt;
  
  
  Can JWT be revoked?
&lt;/h3&gt;

&lt;p&gt;Yes, but you need extra mechanisms like token blacklisting.&lt;/p&gt;




&lt;h3&gt;
  
  
  Should I always use JWT for APIs?
&lt;/h3&gt;

&lt;p&gt;Not always. Simpler systems may work better with sessions.&lt;/p&gt;




&lt;h2&gt;
  
  
  Build Faster
&lt;/h2&gt;

&lt;p&gt;If you want a ready to use authentication setup:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://buildbasekit.com/boilerplates/authkit-lite/" rel="noopener noreferrer"&gt;https://buildbasekit.com/boilerplates/authkit-lite/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JWT authentication&lt;/li&gt;
&lt;li&gt;role based access&lt;/li&gt;
&lt;li&gt;clean architecture&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Free and production ready.&lt;/p&gt;

</description>
      <category>springboot</category>
      <category>java</category>
      <category>backend</category>
      <category>security</category>
    </item>
    <item>
      <title>How to Store and Serve Files in Spring Boot (Local Storage Guide)</title>
      <dc:creator>buildbasekit</dc:creator>
      <pubDate>Thu, 09 Apr 2026 11:45:48 +0000</pubDate>
      <link>https://dev.to/buildbasekit/how-to-store-and-serve-files-in-spring-boot-local-storage-guide-23id</link>
      <guid>https://dev.to/buildbasekit/how-to-store-and-serve-files-in-spring-boot-local-storage-guide-23id</guid>
      <description>&lt;p&gt;Storing files in Spring Boot using local storage is easy.&lt;/p&gt;

&lt;p&gt;But most implementations break when the project grows.&lt;/p&gt;

&lt;p&gt;What starts as a simple upload API quickly turns into:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;messy file paths
&lt;/li&gt;
&lt;li&gt;poor structure
&lt;/li&gt;
&lt;li&gt;scaling issues
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This guide shows how to implement &lt;strong&gt;local file storage properly&lt;/strong&gt;, so your system stays clean and easy to upgrade later.&lt;/p&gt;




&lt;h2&gt;
  
  
  Quick Insight
&lt;/h2&gt;

&lt;p&gt;Local storage works well for small to medium applications.&lt;/p&gt;

&lt;p&gt;But it does not scale in distributed systems.&lt;/p&gt;

&lt;p&gt;Use it for simplicity, not long term architecture.&lt;/p&gt;




&lt;h2&gt;
  
  
  What is Local File Storage in Spring Boot
&lt;/h2&gt;

&lt;p&gt;Local storage means saving uploaded files directly on your server filesystem instead of using cloud storage like S3.&lt;/p&gt;

&lt;p&gt;It is simple, fast, and easy to set up.&lt;/p&gt;




&lt;h2&gt;
  
  
  When You Should Use Local Storage
&lt;/h2&gt;

&lt;p&gt;Local storage is a good choice when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;building small to medium applications
&lt;/li&gt;
&lt;li&gt;creating internal tools or admin panels
&lt;/li&gt;
&lt;li&gt;working on MVPs or prototypes
&lt;/li&gt;
&lt;li&gt;running on a single server
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Many projects start here. The key is to structure it properly from the beginning.&lt;/p&gt;




&lt;h2&gt;
  
  
  Basic File Storage Flow
&lt;/h2&gt;

&lt;p&gt;A typical file handling flow looks like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;receive file via upload endpoint
&lt;/li&gt;
&lt;li&gt;validate file
&lt;/li&gt;
&lt;li&gt;save file to local directory
&lt;/li&gt;
&lt;li&gt;store file reference in database
&lt;/li&gt;
&lt;li&gt;serve file via API
&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Example: File Upload API
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@PostMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/upload"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;?&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;upload&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@RequestParam&lt;/span&gt; &lt;span class="nc"&gt;MultipartFile&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"/uploads/"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getOriginalFilename&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;transferTo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;File&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ok&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Uploaded"&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;This works, but it is not production ready yet.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where to Store Files
&lt;/h2&gt;

&lt;p&gt;Do not store files inside your source code folders.&lt;/p&gt;

&lt;p&gt;Instead:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;use a dedicated directory&lt;/li&gt;
&lt;li&gt;make the path configurable&lt;/li&gt;
&lt;li&gt;keep storage separate from your codebase&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/data/uploads/
/var/app/files/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Serving Files Through API
&lt;/h2&gt;

&lt;p&gt;Never expose raw file paths directly.&lt;/p&gt;

&lt;p&gt;Instead:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;create a download endpoint&lt;/li&gt;
&lt;li&gt;stream files instead of loading fully in memory&lt;/li&gt;
&lt;li&gt;set correct content type and headers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This keeps your system secure and efficient.&lt;/p&gt;




&lt;h2&gt;
  
  
  Keep Storage Logic Separate
&lt;/h2&gt;

&lt;p&gt;Avoid putting everything inside controllers.&lt;/p&gt;

&lt;p&gt;Use a clean structure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Controller → handles request and response&lt;/li&gt;
&lt;li&gt;Service → handles file logic&lt;/li&gt;
&lt;li&gt;Storage layer → interacts with filesystem&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This makes future migration to S3 much easier.&lt;/p&gt;




&lt;h2&gt;
  
  
  File Naming and Validation
&lt;/h2&gt;

&lt;p&gt;Never trust user input.&lt;/p&gt;

&lt;p&gt;Always:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;generate unique file names&lt;/li&gt;
&lt;li&gt;validate file type&lt;/li&gt;
&lt;li&gt;enforce file size limits&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Bad validation is a common security risk.&lt;/p&gt;




&lt;h2&gt;
  
  
  Common Mistakes to Avoid
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;hardcoding file paths&lt;/li&gt;
&lt;li&gt;using original file names directly&lt;/li&gt;
&lt;li&gt;skipping validation&lt;/li&gt;
&lt;li&gt;mixing storage logic with business logic&lt;/li&gt;
&lt;li&gt;serving files without access control&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These issues show up later when scaling.&lt;/p&gt;




&lt;h2&gt;
  
  
  When Local Storage is Enough
&lt;/h2&gt;

&lt;p&gt;Local storage is perfectly fine for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;small to medium apps&lt;/li&gt;
&lt;li&gt;internal tools&lt;/li&gt;
&lt;li&gt;prototypes&lt;/li&gt;
&lt;li&gt;single server deployments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But it becomes a limitation when you scale beyond one server.&lt;/p&gt;




&lt;h2&gt;
  
  
  Local Storage vs Cloud Storage
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Local Storage&lt;/th&gt;
&lt;th&gt;Cloud Storage (S3)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;simple setup&lt;/td&gt;
&lt;td&gt;scalable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;low cost&lt;/td&gt;
&lt;td&gt;highly available&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;single server&lt;/td&gt;
&lt;td&gt;distributed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;hard to scale&lt;/td&gt;
&lt;td&gt;easy to scale&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If you expect growth, design your system so migration is easy.&lt;/p&gt;




&lt;h2&gt;
  
  
  Want Production Ready File Upload?
&lt;/h2&gt;

&lt;p&gt;If you want to move beyond local storage and build a scalable system:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://buildbasekit.com/blogs/spring-boot-file-upload-production-guide/" rel="noopener noreferrer"&gt;https://buildbasekit.com/blogs/spring-boot-file-upload-production-guide/&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Local storage is simple, but it still needs structure.&lt;/p&gt;

&lt;p&gt;If you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;separate concerns&lt;/li&gt;
&lt;li&gt;validate inputs&lt;/li&gt;
&lt;li&gt;design clean APIs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;you can avoid most common problems and upgrade your system later without rewriting everything.&lt;/p&gt;




&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Where should I store files in Spring Boot?
&lt;/h3&gt;

&lt;p&gt;Use a dedicated directory outside your source code and make it configurable.&lt;/p&gt;




&lt;h3&gt;
  
  
  Is local storage good for production?
&lt;/h3&gt;

&lt;p&gt;It works for small applications, but not for distributed systems.&lt;/p&gt;




&lt;h3&gt;
  
  
  When should I switch to S3?
&lt;/h3&gt;

&lt;p&gt;When you need scalability, multiple servers, or global access.&lt;/p&gt;




&lt;h2&gt;
  
  
  Related Guides
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://buildbasekit.com/blogs/spring-boot-file-upload-api/" rel="noopener noreferrer"&gt;https://buildbasekit.com/blogs/spring-boot-file-upload-api/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://buildbasekit.com/blogs/file-upload-mistakes-spring-boot/" rel="noopener noreferrer"&gt;https://buildbasekit.com/blogs/file-upload-mistakes-spring-boot/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://buildbasekit.com/blogs/spring-boot-file-upload-production-guide/" rel="noopener noreferrer"&gt;https://buildbasekit.com/blogs/spring-boot-file-upload-production-guide/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://buildbasekit.com/blogs/jwt-mistakes-spring-boot/" rel="noopener noreferrer"&gt;https://buildbasekit.com/blogs/jwt-mistakes-spring-boot/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Build Faster
&lt;/h2&gt;

&lt;p&gt;If you are tired of rebuilding file upload logic:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://buildbasekit.com/boilerplates/filora-fs-lite/" rel="noopener noreferrer"&gt;https://buildbasekit.com/boilerplates/filora-fs-lite/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;file upload system&lt;/li&gt;
&lt;li&gt;local storage setup&lt;/li&gt;
&lt;li&gt;clean architecture&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Free and ready to use.&lt;/p&gt;

</description>
      <category>springboot</category>
      <category>java</category>
      <category>backend</category>
      <category>fileupload</category>
    </item>
    <item>
      <title>Spring Boot File Upload: Production Ready System Design Guide</title>
      <dc:creator>buildbasekit</dc:creator>
      <pubDate>Thu, 09 Apr 2026 11:41:18 +0000</pubDate>
      <link>https://dev.to/buildbasekit/spring-boot-file-upload-production-ready-system-design-guide-490l</link>
      <guid>https://dev.to/buildbasekit/spring-boot-file-upload-production-ready-system-design-guide-490l</guid>
      <description>&lt;p&gt;Building a file upload API in Spring Boot is easy.&lt;/p&gt;

&lt;p&gt;Building one that actually works in production is where things break.&lt;/p&gt;

&lt;p&gt;Most developers start with a simple controller and local storage. It works fine until:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;files get large
&lt;/li&gt;
&lt;li&gt;traffic increases
&lt;/li&gt;
&lt;li&gt;security becomes a concern
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This guide breaks down what actually matters when designing a &lt;strong&gt;production ready file upload system&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Quick Insight
&lt;/h2&gt;

&lt;p&gt;A real file upload system is not just about uploading files.&lt;/p&gt;

&lt;p&gt;You need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;security&lt;/li&gt;
&lt;li&gt;scalable storage&lt;/li&gt;
&lt;li&gt;efficient delivery&lt;/li&gt;
&lt;li&gt;clean API structure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Miss one of these, and things start failing in production.&lt;/p&gt;




&lt;h2&gt;
  
  
  Common Problems with Basic File Upload Systems
&lt;/h2&gt;

&lt;p&gt;Most implementations fail because they ignore real world constraints.&lt;/p&gt;

&lt;p&gt;Typical issues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;large uploads crash the server
&lt;/li&gt;
&lt;li&gt;no validation leads to security risks
&lt;/li&gt;
&lt;li&gt;local storage fails in distributed setups
&lt;/li&gt;
&lt;li&gt;messy APIs make scaling painful
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you have seen any of these, your system is not production ready yet.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. File Upload Security in Spring Boot
&lt;/h2&gt;

&lt;p&gt;Security is not optional.&lt;/p&gt;

&lt;p&gt;Every uploaded file should be treated as untrusted input.&lt;/p&gt;

&lt;h3&gt;
  
  
  What you must do:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;validate file type and size
&lt;/li&gt;
&lt;li&gt;restrict access using authentication (JWT or sessions)
&lt;/li&gt;
&lt;li&gt;never expose internal file paths
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you have not set up auth yet, start here:&lt;br&gt;&lt;br&gt;
👉 &lt;a href="https://buildbasekit.com/blogs/spring-boot-jwt-authentication/" rel="noopener noreferrer"&gt;https://buildbasekit.com/blogs/spring-boot-jwt-authentication/&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  2. Scalable File Storage Strategy (S3 and Cloud)
&lt;/h2&gt;

&lt;p&gt;Local storage works only in early stages.&lt;/p&gt;

&lt;p&gt;It breaks when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you scale horizontally
&lt;/li&gt;
&lt;li&gt;you deploy across multiple servers
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Better approach:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;use cloud storage like AWS S3
&lt;/li&gt;
&lt;li&gt;keep storage separate from application logic
&lt;/li&gt;
&lt;li&gt;design storage as a pluggable layer
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This gives you flexibility without rewriting your system later.&lt;/p&gt;


&lt;h2&gt;
  
  
  3. File Access and Delivery Optimization
&lt;/h2&gt;

&lt;p&gt;Serving files from your backend is a bottleneck.&lt;/p&gt;
&lt;h3&gt;
  
  
  Production approach:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;use pre signed URLs
&lt;/li&gt;
&lt;li&gt;stream files instead of loading in memory
&lt;/li&gt;
&lt;li&gt;use CDN for faster delivery
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This reduces load on your server and improves performance.&lt;/p&gt;


&lt;h2&gt;
  
  
  4. File Metadata and Management
&lt;/h2&gt;

&lt;p&gt;Uploading files is only half the problem.&lt;/p&gt;

&lt;p&gt;You also need to manage them.&lt;/p&gt;
&lt;h3&gt;
  
  
  Store metadata like:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;file name
&lt;/li&gt;
&lt;li&gt;size
&lt;/li&gt;
&lt;li&gt;owner
&lt;/li&gt;
&lt;li&gt;storage location
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Support:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;listing files
&lt;/li&gt;
&lt;li&gt;deleting files
&lt;/li&gt;
&lt;li&gt;updating metadata
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without this, your system becomes hard to maintain.&lt;/p&gt;


&lt;h2&gt;
  
  
  5. Clean File Upload API Design
&lt;/h2&gt;

&lt;p&gt;Bad API design kills scalability.&lt;/p&gt;
&lt;h3&gt;
  
  
  Keep things separated:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;upload endpoint
&lt;/li&gt;
&lt;li&gt;download endpoint
&lt;/li&gt;
&lt;li&gt;file management endpoints
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Follow clean architecture:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;controller layer
&lt;/li&gt;
&lt;li&gt;service layer
&lt;/li&gt;
&lt;li&gt;storage abstraction
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Avoid mixing responsibilities.&lt;/p&gt;


&lt;h2&gt;
  
  
  Recommended Architecture
&lt;/h2&gt;

&lt;p&gt;A production ready system usually 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;
Client → Controller → Service → Storage Layer → Cloud (S3)
↓
Database (metadata)

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Key components:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Controller for request handling
&lt;/li&gt;
&lt;li&gt;Service for business logic
&lt;/li&gt;
&lt;li&gt;Storage abstraction (S3 or local)
&lt;/li&gt;
&lt;li&gt;Database for metadata
&lt;/li&gt;
&lt;li&gt;Authentication layer
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This keeps the system maintainable and scalable.&lt;/p&gt;




&lt;h2&gt;
  
  
  Common Mistakes to Avoid
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;storing files directly on the app server
&lt;/li&gt;
&lt;li&gt;skipping validation
&lt;/li&gt;
&lt;li&gt;mixing storage logic with business logic
&lt;/li&gt;
&lt;li&gt;serving files without optimization
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These mistakes are the reason most systems fail later.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;A production ready file upload system is not about adding features.&lt;/p&gt;

&lt;p&gt;It is about building the right foundation.&lt;/p&gt;

&lt;p&gt;If you design it properly from the start:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you avoid scaling issues
&lt;/li&gt;
&lt;li&gt;you reduce security risks
&lt;/li&gt;
&lt;li&gt;you save time later
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most developers underestimate this until they hit real world problems.&lt;/p&gt;




&lt;h2&gt;
  
  
  Want a Ready to Use Solution?
&lt;/h2&gt;

&lt;p&gt;If you do not want to rebuild this system again and again:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://buildbasekit.com/boilerplates/filora-fs-pro/" rel="noopener noreferrer"&gt;https://buildbasekit.com/boilerplates/filora-fs-pro/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;authentication setup
&lt;/li&gt;
&lt;li&gt;S3 integration
&lt;/li&gt;
&lt;li&gt;clean API structure
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Built for real world applications.&lt;/p&gt;




&lt;h2&gt;
  
  
  Related Guides
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://buildbasekit.com/blogs/spring-boot-file-upload-api/" rel="noopener noreferrer"&gt;https://buildbasekit.com/blogs/spring-boot-file-upload-api/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://buildbasekit.com/blogs/file-upload-mistakes-spring-boot/" rel="noopener noreferrer"&gt;https://buildbasekit.com/blogs/file-upload-mistakes-spring-boot/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://buildbasekit.com/blogs/spring-boot-file-storage-local-guide/" rel="noopener noreferrer"&gt;https://buildbasekit.com/blogs/spring-boot-file-storage-local-guide/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://buildbasekit.com/blogs/how-to-deploy-a-production-ready-file-server-on-a-vps-for-free/" rel="noopener noreferrer"&gt;https://buildbasekit.com/blogs/how-to-deploy-a-production-ready-file-server-on-a-vps-for-free/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>springboot</category>
      <category>backend</category>
      <category>java</category>
      <category>systemdesign</category>
    </item>
    <item>
      <title>Stop Building Messy Discord Bots in Java</title>
      <dc:creator>buildbasekit</dc:creator>
      <pubDate>Tue, 31 Mar 2026 08:59:54 +0000</pubDate>
      <link>https://dev.to/buildbasekit/stop-building-messy-discord-bots-in-java-5eng</link>
      <guid>https://dev.to/buildbasekit/stop-building-messy-discord-bots-in-java-5eng</guid>
      <description>&lt;p&gt;Building a Discord bot in Java is easy.&lt;/p&gt;

&lt;p&gt;Keeping it clean as it grows is the hard part.&lt;/p&gt;

&lt;p&gt;You start with a few commands, and then:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;logic starts mixing with event handling
&lt;/li&gt;
&lt;li&gt;commands become hard to extend
&lt;/li&gt;
&lt;li&gt;small changes break multiple features
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’ve seen bots become messy very quickly.&lt;/p&gt;

&lt;p&gt;Here’s how to structure it properly from the beginning.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Discord bots become hard to maintain
&lt;/h2&gt;

&lt;p&gt;Most bots start small.&lt;/p&gt;

&lt;p&gt;But as features grow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;commands get tightly coupled with logic
&lt;/li&gt;
&lt;li&gt;event handling becomes inconsistent
&lt;/li&gt;
&lt;li&gt;code duplication increases
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without structure, scaling becomes painful.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Discord Bot Structure Matters for Scalability
&lt;/h2&gt;

&lt;p&gt;When structure is ignored early, even small changes require touching multiple parts of the codebase.&lt;/p&gt;

&lt;p&gt;That’s when development slows down.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;commands become tightly coupled with logic&lt;/li&gt;
&lt;li&gt;event handling becomes inconsistent&lt;/li&gt;
&lt;li&gt;features are harder to extend&lt;/li&gt;
&lt;li&gt;debugging takes longer than expected&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Core Components of a Scalable Discord Bot (Java JDA)
&lt;/h2&gt;

&lt;p&gt;A scalable Discord bot should have clear separation between different responsibilities. Even a simple bot benefits from a structured approach.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;command layer to handle slash commands and inputs&lt;/li&gt;
&lt;li&gt;event listeners to react to Discord events&lt;/li&gt;
&lt;li&gt;service layer for business logic&lt;/li&gt;
&lt;li&gt;configuration layer for environment variables and setup&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  How a Discord Bot Works in JDA (Step by Step)
&lt;/h2&gt;

&lt;p&gt;Understanding the flow helps you design a clean and predictable structure.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;User triggers a command or event in Discord&lt;/li&gt;
&lt;li&gt;JDA listener receives the event&lt;/li&gt;
&lt;li&gt;Command handler processes input&lt;/li&gt;
&lt;li&gt;Service layer executes logic&lt;/li&gt;
&lt;li&gt;Response is sent back to Discord&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If these responsibilities are not clearly separated, your bot quickly becomes difficult to maintain.&lt;/p&gt;




&lt;h2&gt;
  
  
  Best practice: separate commands and business logic
&lt;/h2&gt;

&lt;p&gt;A common mistake is putting all logic inside command handlers.&lt;/p&gt;

&lt;p&gt;It works at first, but becomes hard to maintain as features grow.&lt;/p&gt;

&lt;p&gt;A better approach is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;commands handle input and response&lt;/li&gt;
&lt;li&gt;services handle actual logic&lt;/li&gt;
&lt;li&gt;listeners react to events independently
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;
&lt;span class="c1"&gt;// bad: logic inside command&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;CommandEvent&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="k"&gt;if&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;getName&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ping"&lt;/span&gt;&lt;span class="o"&gt;))&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;reply&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Pong"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="c1"&gt;// business logic mixed here&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;h2&gt;
  
  
  Design Your Discord Bot for Scalability and Extension
&lt;/h2&gt;

&lt;p&gt;Your bot should be easy to extend without rewriting existing code. This means organizing features in a modular way.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;group related commands into modules&lt;/li&gt;
&lt;li&gt;keep shared logic reusable&lt;/li&gt;
&lt;li&gt;avoid hardcoded values in logic&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Recommended project structure
&lt;/h2&gt;

&lt;p&gt;This structure keeps your Discord bot modular and easy to scale as features grow.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
src/
 ├── commands/
 ├── listeners/
 ├── service/
 ├── config/
 └── utils/

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

&lt;/div&gt;






&lt;p&gt;If you don’t want to set this up from scratch:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://buildbasekit.com/boilerplates/basely/" rel="noopener noreferrer"&gt;https://buildbasekit.com/boilerplates/basely/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It includes a clean Discord bot structure built with JDA.&lt;/p&gt;




&lt;h2&gt;
  
  
  Common mistakes to avoid
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;putting all code in a single package&lt;/li&gt;
&lt;li&gt;mixing event handling with business logic&lt;/li&gt;
&lt;li&gt;no clear naming or structure for commands&lt;/li&gt;
&lt;li&gt;duplicating logic across different commands&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Common symptom
&lt;/h3&gt;

&lt;p&gt;Adding a new command requires modifying multiple parts of the codebase.&lt;/p&gt;




&lt;h2&gt;
  
  
  Without vs with proper structure
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Without structure
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;logic inside command handlers&lt;/li&gt;
&lt;li&gt;hard to scale features&lt;/li&gt;
&lt;li&gt;duplicate code&lt;/li&gt;
&lt;li&gt;messy event handling&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  With structure
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;clean separation of layers&lt;/li&gt;
&lt;li&gt;easy to extend commands&lt;/li&gt;
&lt;li&gt;reusable logic&lt;/li&gt;
&lt;li&gt;predictable architecture&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;Discord bots don’t become complex because of features.&lt;/p&gt;

&lt;p&gt;They become complex because of poor structure.&lt;/p&gt;

&lt;p&gt;If you separate commands, events, and logic early, your bot stays easy to extend as it grows.&lt;/p&gt;




&lt;h2&gt;
  
  
  Want a clean Discord bot setup without rebuilding everything?
&lt;/h2&gt;

&lt;p&gt;I built a minimal Java boilerplate using JDA with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;proper command and event separation
&lt;/li&gt;
&lt;li&gt;clean service layer
&lt;/li&gt;
&lt;li&gt;scalable structure
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 &lt;a href="https://buildbasekit.com/boilerplates/basely/" rel="noopener noreferrer"&gt;https://buildbasekit.com/boilerplates/basely/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Use it as a starting point instead of building your bot structure from scratch.&lt;/p&gt;




&lt;h2&gt;
  
  
  Related articles
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://buildbasekit.com/blogs/how-to-build-and-deploy-a-java-discord-bot-using-spring-boot/" rel="noopener noreferrer"&gt;How to Build and Deploy a Java Discord Bot Using Spring Boot&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Build a production-ready Discord bot using Java 21, Spring Boot, and JDA. Includes project structure, deployment, and best practices.&lt;/p&gt;




</description>
      <category>java</category>
      <category>springboot</category>
      <category>discordbot</category>
      <category>jda</category>
    </item>
    <item>
      <title>Stop Making These File Upload Mistakes in Spring Boot</title>
      <dc:creator>buildbasekit</dc:creator>
      <pubDate>Tue, 31 Mar 2026 08:43:15 +0000</pubDate>
      <link>https://dev.to/buildbasekit/stop-making-these-file-upload-mistakes-in-spring-boot-2hlb</link>
      <guid>https://dev.to/buildbasekit/stop-making-these-file-upload-mistakes-in-spring-boot-2hlb</guid>
      <description>&lt;p&gt;File upload in Spring Boot looks simple… until it starts breaking.&lt;/p&gt;

&lt;p&gt;At first, it’s just one endpoint.&lt;/p&gt;

&lt;p&gt;Then suddenly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;file logic spreads across your codebase
&lt;/li&gt;
&lt;li&gt;validation is inconsistent
&lt;/li&gt;
&lt;li&gt;storage becomes hard to change
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’ve seen this turn messy very quickly.&lt;/p&gt;

&lt;p&gt;Here are the most common mistakes and how to avoid them.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why file upload implementations go wrong
&lt;/h2&gt;

&lt;p&gt;File upload is deceptively simple.&lt;/p&gt;

&lt;p&gt;But as soon as you add:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;validation
&lt;/li&gt;
&lt;li&gt;storage
&lt;/li&gt;
&lt;li&gt;file retrieval
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;the logic starts spreading across multiple layers.&lt;/p&gt;

&lt;p&gt;Without structure, it becomes hard to maintain.&lt;/p&gt;




&lt;h2&gt;
  
  
  How File Upload Works in Spring Boot (Step by Step)
&lt;/h2&gt;

&lt;p&gt;Understanding the flow helps you avoid most implementation mistakes.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Client sends file using multipart request&lt;/li&gt;
&lt;li&gt;Controller receives the file&lt;/li&gt;
&lt;li&gt;Service validates and processes it&lt;/li&gt;
&lt;li&gt;Storage layer saves the file&lt;/li&gt;
&lt;li&gt;API returns file reference or URL&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  1. Mixing file handling logic in controllers
&lt;/h3&gt;

&lt;p&gt;A common mistake is putting file processing directly inside controllers.&lt;/p&gt;

&lt;p&gt;It works at first, but quickly becomes hard to maintain.&lt;/p&gt;

&lt;p&gt;Common issues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;file saving logic inside endpoints
&lt;/li&gt;
&lt;li&gt;manual path handling in controllers
&lt;/li&gt;
&lt;li&gt;duplicate logic across APIs
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Controllers should stay thin. File handling belongs in a service layer.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. No validation for file size and type
&lt;/h2&gt;

&lt;p&gt;Accepting any file without validation can lead to security risks and performance issues.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;uploading extremely large files&lt;/li&gt;
&lt;li&gt;accepting unsupported file types&lt;/li&gt;
&lt;li&gt;no limits configured for uploads&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Always define clear limits and validate file types before storing them.&lt;/p&gt;




&lt;h2&gt;
  
  
  Recommended Spring Boot File Upload Structure
&lt;/h2&gt;

&lt;p&gt;This structure keeps file upload logic modular and easy to extend as requirements grow.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
src/
 ├── controller/
 ├── service/
 ├── storage/
 ├── model/
 └── config/

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

&lt;/div&gt;






&lt;p&gt;If you don’t want to build this structure from scratch:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://buildbasekit.com/boilerplates/filora-fs-lite/" rel="noopener noreferrer"&gt;https://buildbasekit.com/boilerplates/filora-fs-lite/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It already includes a clean file upload setup with proper separation.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Hardcoding file paths
&lt;/h2&gt;

&lt;p&gt;Hardcoded paths make your application difficult to configure and deploy across environments.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;fixed local directories in code&lt;/li&gt;
&lt;li&gt;no environment-based configuration&lt;/li&gt;
&lt;li&gt;difficult to switch storage later&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Use configuration properties or environment variables for file storage paths.&lt;/p&gt;

&lt;h3&gt;
  
  
  Common symptom
&lt;/h3&gt;

&lt;p&gt;You start with one upload endpoint and end up debugging file handling issues across multiple parts of your application.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. No clear storage abstraction
&lt;/h2&gt;

&lt;p&gt;Many implementations tightly couple file upload logic with storage details. This makes it hard to switch from local storage to cloud.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;storage logic mixed with upload logic&lt;/li&gt;
&lt;li&gt;no abstraction layer for storage&lt;/li&gt;
&lt;li&gt;difficult to extend to S3 or other providers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A separate storage layer makes your system flexible and easier to maintain.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Ignoring file naming strategy
&lt;/h2&gt;

&lt;p&gt;Saving files with original names can cause conflicts and unexpected overwrites.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;duplicate file names overwrite existing files&lt;/li&gt;
&lt;li&gt;no unique identifiers&lt;/li&gt;
&lt;li&gt;hard to track files reliably&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Use unique naming strategies such as UUIDs to avoid collisions.&lt;/p&gt;




&lt;h2&gt;
  
  
  Without vs with proper structure
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Without structure
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;file logic inside controllers&lt;/li&gt;
&lt;li&gt;hardcoded paths&lt;/li&gt;
&lt;li&gt;no validation&lt;/li&gt;
&lt;li&gt;difficult to scale&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  With structure
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;clean separation of layers&lt;/li&gt;
&lt;li&gt;flexible storage system&lt;/li&gt;
&lt;li&gt;proper validation&lt;/li&gt;
&lt;li&gt;easy to maintain&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;File upload is not the problem.&lt;/p&gt;

&lt;p&gt;Bad structure is.&lt;/p&gt;

&lt;p&gt;If you separate responsibilities and handle validation and storage properly, your system stays clean as it grows.&lt;/p&gt;




&lt;h2&gt;
  
  
  Want a clean file upload setup without rebuilding it every time?
&lt;/h2&gt;

&lt;p&gt;I built a minimal Spring Boot boilerplate with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;proper service and storage separation
&lt;/li&gt;
&lt;li&gt;file upload and download endpoints
&lt;/li&gt;
&lt;li&gt;production-ready structure
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 &lt;a href="https://buildbasekit.com/boilerplates/filora-fs-lite/" rel="noopener noreferrer"&gt;https://buildbasekit.com/boilerplates/filora-fs-lite/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Use it as a starting point instead of reinventing file upload for every project.&lt;/p&gt;




&lt;h2&gt;
  
  
  Related articles
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://buildbasekit.com/blogs/spring-boot-file-upload-api/" rel="noopener noreferrer"&gt;Spring Boot File Upload API (Clean Structure Guide)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Build a Spring Boot file upload API with clean structure. Learn MultipartFile handling, validation, and scalable storage design.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://buildbasekit.com/blogs/how-to-deploy-a-production-ready-file-server-on-a-vps-for-free/" rel="noopener noreferrer"&gt;How to Deploy a Production File Server on a VPS for Free&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Learn how to deploy a scalable file storage backend using Spring Boot. Step by step VPS setup with zero cost.&lt;/p&gt;




</description>
      <category>java</category>
      <category>springboot</category>
      <category>guide</category>
      <category>storage</category>
    </item>
    <item>
      <title>Stop Overcomplicating File Upload in Spring Boot</title>
      <dc:creator>buildbasekit</dc:creator>
      <pubDate>Tue, 31 Mar 2026 08:28:08 +0000</pubDate>
      <link>https://dev.to/buildbasekit/stop-overcomplicating-file-upload-in-spring-boot-g8c</link>
      <guid>https://dev.to/buildbasekit/stop-overcomplicating-file-upload-in-spring-boot-g8c</guid>
      <description>&lt;p&gt;Building a file upload API in Spring Boot looks simple… until it isn’t.&lt;/p&gt;

&lt;p&gt;You start with one endpoint, and suddenly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;file paths are scattered everywhere&lt;/li&gt;
&lt;li&gt;validation is inconsistent&lt;/li&gt;
&lt;li&gt;storage logic leaks into controllers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’ve seen this turn into a mess very quickly.&lt;/p&gt;

&lt;p&gt;In some cases, teams end up rewriting their entire file handling logic.&lt;/p&gt;

&lt;p&gt;Here’s how to build it clean from the start.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why file upload APIs get messy
&lt;/h2&gt;

&lt;p&gt;File upload starts simple, but complexity grows fast:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;handling different file types&lt;/li&gt;
&lt;li&gt;managing storage paths&lt;/li&gt;
&lt;li&gt;adding validation&lt;/li&gt;
&lt;li&gt;supporting downloads&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without structure, this logic spreads across your codebase.&lt;/p&gt;




&lt;h2&gt;
  
  
  Core components of a file upload API
&lt;/h2&gt;

&lt;p&gt;Even a simple setup should include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;upload endpoint to receive files&lt;/li&gt;
&lt;li&gt;download endpoint to retrieve files&lt;/li&gt;
&lt;li&gt;storage layer (local or cloud)&lt;/li&gt;
&lt;li&gt;validation for file size and type&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  How Spring Boot File Upload Works (Step by Step)
&lt;/h2&gt;

&lt;p&gt;Understanding the flow helps you design the API correctly from the start.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Client sends file using multipart request&lt;/li&gt;
&lt;li&gt;Controller receives the file&lt;/li&gt;
&lt;li&gt;Service processes and validates it&lt;/li&gt;
&lt;li&gt;Storage layer saves the file&lt;/li&gt;
&lt;li&gt;API returns file reference or URL&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Best Practice: Structure Your Spring Boot File Upload API
&lt;/h2&gt;

&lt;p&gt;One of the biggest mistakes is mixing file handling logic directly into controllers. This makes it harder to change storage strategies later.&lt;/p&gt;

&lt;p&gt;A cleaner approach is to separate responsibilities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;controller handles request and response&lt;/li&gt;
&lt;li&gt;service handles file processing logic&lt;/li&gt;
&lt;li&gt;storage layer manages file saving and retrieval&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Recommended project structure
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
src/
 ├── controller/
 ├── service/
 ├── storage/
 ├── model/
 └── config/

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

&lt;/div&gt;






&lt;h2&gt;
  
  
  Common mistakes to avoid
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;storing files without proper naming strategy&lt;/li&gt;
&lt;li&gt;not validating file size or type&lt;/li&gt;
&lt;li&gt;hardcoding file paths&lt;/li&gt;
&lt;li&gt;no separation between upload and storage logic&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  A better approach
&lt;/h2&gt;

&lt;p&gt;Start simple, but keep structure clear from day one.&lt;/p&gt;

&lt;p&gt;This makes it easy to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;switch from local to cloud storage&lt;/li&gt;
&lt;li&gt;add authentication later&lt;/li&gt;
&lt;li&gt;scale without rewriting everything&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Without vs with proper structure
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Without structure
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;file logic inside controllers&lt;/li&gt;
&lt;li&gt;hardcoded paths&lt;/li&gt;
&lt;li&gt;difficult to switch storage&lt;/li&gt;
&lt;li&gt;code duplication&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  With structure
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;clean separation of layers&lt;/li&gt;
&lt;li&gt;easy to extend and maintain&lt;/li&gt;
&lt;li&gt;storage can be swapped&lt;/li&gt;
&lt;li&gt;reusable across projects&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Conclusion: Build a Clean File Upload API in Spring Boot
&lt;/h2&gt;

&lt;p&gt;File upload APIs do not need to be complicated. With a simple structure and clear separation of concerns, you can build something that is both easy to maintain and easy to extend.&lt;/p&gt;




&lt;h2&gt;
  
  
  Want a clean file upload setup without rebuilding it every time?
&lt;/h2&gt;

&lt;p&gt;I built a minimal Spring Boot boilerplate with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;clean controller, service, and storage separation
&lt;/li&gt;
&lt;li&gt;file upload and download endpoints
&lt;/li&gt;
&lt;li&gt;production-ready structure you can extend
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 &lt;a href="https://buildbasekit.com/boilerplates/filora-fs-lite/" rel="noopener noreferrer"&gt;https://buildbasekit.com/boilerplates/filora-fs-lite/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Use it as a starting point instead of reinventing file upload for every project.&lt;/p&gt;




&lt;h2&gt;
  
  
  Related articles
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://buildbasekit.com/blogs/file-upload-mistakes-spring-boot/" rel="noopener noreferrer"&gt;Spring Boot File Upload Mistakes (Common Issues)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Avoid common file upload mistakes in Spring Boot. Learn validation, storage design, and how to structure clean file upload APIs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://buildbasekit.com/blogs/how-to-deploy-a-production-ready-file-server-on-a-vps-for-free/" rel="noopener noreferrer"&gt;How to Deploy a Production File Server on a VPS for Free&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Learn how to deploy a scalable file storage backend using Spring Boot. Step by step VPS setup with zero cost.&lt;/p&gt;

</description>
      <category>java</category>
      <category>springboot</category>
      <category>guide</category>
      <category>storgae</category>
    </item>
    <item>
      <title>Stop Making These JWT Mistakes in Spring Boot</title>
      <dc:creator>buildbasekit</dc:creator>
      <pubDate>Tue, 31 Mar 2026 07:15:18 +0000</pubDate>
      <link>https://dev.to/buildbasekit/stop-making-these-jwt-mistakes-in-spring-boot-2g6o</link>
      <guid>https://dev.to/buildbasekit/stop-making-these-jwt-mistakes-in-spring-boot-2g6o</guid>
      <description>&lt;p&gt;Most JWT authentication setups in Spring Boot work... until they don’t.&lt;/p&gt;

&lt;p&gt;You ship fast, everything seems fine, and then:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;tokens stop validating&lt;/li&gt;
&lt;li&gt;security bugs appear&lt;/li&gt;
&lt;li&gt;your code becomes impossible to maintain&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’ve seen this happen multiple times.&lt;/p&gt;

&lt;p&gt;Here are the most common mistakes and how to avoid them.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why JWT mistakes are dangerous
&lt;/h2&gt;

&lt;p&gt;JWT issues are not just bugs.&lt;/p&gt;

&lt;p&gt;They can lead to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;unauthorized access&lt;/li&gt;
&lt;li&gt;broken authentication flows&lt;/li&gt;
&lt;li&gt;hard-to-debug production issues&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Fixing structure early saves you from rewriting your auth system later.&lt;/p&gt;




&lt;p&gt;JWT makes authentication easy to scale.&lt;/p&gt;

&lt;p&gt;But a poor implementation quickly turns into:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;fragile security&lt;/li&gt;
&lt;li&gt;messy code&lt;/li&gt;
&lt;li&gt;hard-to-debug issues&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most problems come from structure, not JWT itself.&lt;/p&gt;




&lt;h2&gt;
  
  
  How JWT authentication actually works
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;User logs in with credentials
&lt;/li&gt;
&lt;li&gt;Server generates a signed token
&lt;/li&gt;
&lt;li&gt;Client stores the token
&lt;/li&gt;
&lt;li&gt;Token is sent with every request
&lt;/li&gt;
&lt;li&gt;Server validates before processing
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If any step is poorly implemented, your entire system becomes unreliable.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Mixing authentication logic with business logic
&lt;/h2&gt;

&lt;p&gt;One of the most common mistakes is handling authentication directly inside controllers or services that should focus on business functionality.&lt;/p&gt;

&lt;p&gt;Common issues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;token parsing inside controllers&lt;/li&gt;
&lt;li&gt;manual validation scattered across endpoints&lt;/li&gt;
&lt;li&gt;duplicate logic in multiple places&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Authentication should be handled in a dedicated layer, separate from business logic.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Poor token management
&lt;/h2&gt;

&lt;p&gt;JWT tokens are central to your authentication system. Mismanaging them can lead to serious issues.&lt;/p&gt;

&lt;p&gt;Common symptoms:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;users randomly logged out&lt;/li&gt;
&lt;li&gt;expired tokens still accepted&lt;/li&gt;
&lt;li&gt;security vulnerabilities&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tokens should be validated consistently and configured with proper expiration strategies.&lt;/p&gt;




&lt;h2&gt;
  
  
  Recommended Spring Boot JWT Authentication Structure
&lt;/h2&gt;

&lt;p&gt;This structure keeps JWT handling isolated and easier to secure and maintain.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
src/
 ├── controller/
 ├── service/
 ├── security/
 ├── model/
 └── repository/

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

&lt;/div&gt;






&lt;h3&gt;
  
  
  If you don’t want to build this from scratch:
&lt;/h3&gt;

&lt;p&gt;👉 &lt;a href="https://buildbasekit.com/boilerplates/authkit-lite/" rel="noopener noreferrer"&gt;https://buildbasekit.com/boilerplates/authkit-lite/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It includes a clean JWT setup with proper structure and security practices.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Hardcoding secrets and configuration
&lt;/h2&gt;

&lt;p&gt;Storing secrets directly in code is risky and makes your system less secure and harder to manage across environments.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JWT secret keys in source code&lt;/li&gt;
&lt;li&gt;environment-specific values hardcoded&lt;/li&gt;
&lt;li&gt;no use of environment variables&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Always use environment-based configuration for sensitive data.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Skipping role-based access control
&lt;/h2&gt;

&lt;p&gt;Authentication alone is not enough. Without proper authorization, your application cannot control what users are allowed to do.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;all endpoints accessible after login&lt;/li&gt;
&lt;li&gt;no role or permission checks&lt;/li&gt;
&lt;li&gt;inconsistent access control logic&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Role-based access should be part of your authentication design from the beginning.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Overcomplicating the setup
&lt;/h2&gt;

&lt;p&gt;Many implementations introduce unnecessary complexity with multiple filters, configurations, and layers that are difficult to debug.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;too many custom filters without clear purpose&lt;/li&gt;
&lt;li&gt;confusing security configuration&lt;/li&gt;
&lt;li&gt;lack of clear flow for authentication&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Keep the setup simple and structured instead of adding complexity early.&lt;/p&gt;




&lt;h2&gt;
  
  
  Without vs Proper JWT structure
&lt;/h2&gt;

&lt;p&gt;Without structure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;auth logic everywhere&lt;/li&gt;
&lt;li&gt;inconsistent validation&lt;/li&gt;
&lt;li&gt;security risks&lt;/li&gt;
&lt;li&gt;hard to scale&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With proper structure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;clean separation&lt;/li&gt;
&lt;li&gt;centralized token handling&lt;/li&gt;
&lt;li&gt;predictable behavior&lt;/li&gt;
&lt;li&gt;easy to maintain&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Quick checklist
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;keep auth separate from business logic&lt;/li&gt;
&lt;li&gt;use token expiration&lt;/li&gt;
&lt;li&gt;never hardcode secrets&lt;/li&gt;
&lt;li&gt;implement role-based access&lt;/li&gt;
&lt;li&gt;avoid overcomplicating configuration&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;JWT is not the problem.&lt;/p&gt;

&lt;p&gt;Bad structure is.&lt;/p&gt;

&lt;p&gt;If you keep authentication isolated, handle tokens properly, and avoid overengineering, your system will stay clean and secure as it grows.&lt;/p&gt;




&lt;h2&gt;
  
  
  Want a clean JWT setup without the mess?
&lt;/h2&gt;

&lt;p&gt;I built a minimal Spring Boot boilerplate with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;proper security layer separation
&lt;/li&gt;
&lt;li&gt;clean JWT handling
&lt;/li&gt;
&lt;li&gt;role-based access control
&lt;/li&gt;
&lt;li&gt;production-ready structure
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 &lt;a href="https://buildbasekit.com/boilerplates/authkit-lite/" rel="noopener noreferrer"&gt;https://buildbasekit.com/boilerplates/authkit-lite/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can use it as a starting point instead of rebuilding auth every time.&lt;/p&gt;




&lt;h2&gt;
  
  
  Related articles
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://buildbasekit.com/blogs/spring-boot-jwt-authentication/" rel="noopener noreferrer"&gt;Spring Boot JWT Authentication (Clean Setup Guide)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Build JWT authentication in Spring Boot with a clean and reusable setup. Learn token handling, security config, and scalable structure.&lt;/p&gt;




</description>
      <category>java</category>
      <category>springboot</category>
      <category>jwt</category>
      <category>security</category>
    </item>
    <item>
      <title>Stop Rewriting JWT Authentication in Spring Boot (Use This Instead)</title>
      <dc:creator>buildbasekit</dc:creator>
      <pubDate>Tue, 31 Mar 2026 06:44:50 +0000</pubDate>
      <link>https://dev.to/buildbasekit/stop-rewriting-jwt-authentication-in-spring-boot-use-this-instead-5465</link>
      <guid>https://dev.to/buildbasekit/stop-rewriting-jwt-authentication-in-spring-boot-use-this-instead-5465</guid>
      <description>&lt;p&gt;If you’ve implemented authentication in Spring Boot more than once,&lt;br&gt;
you’ve probably rebuilt the same setup every time.&lt;/p&gt;

&lt;p&gt;JWT config, Spring Security setup, role handling...&lt;/p&gt;

&lt;p&gt;It gets repetitive fast.&lt;/p&gt;




&lt;h2&gt;
  
  
  Who this is for
&lt;/h2&gt;

&lt;p&gt;This guide is for you if:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you’ve implemented auth more than once&lt;/li&gt;
&lt;li&gt;you’re tired of repeating setup&lt;/li&gt;
&lt;li&gt;you want a clean and reusable structure&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Authentication is one of the first things every backend project needs.&lt;/p&gt;

&lt;p&gt;But instead of being a one-time setup, &lt;br&gt;
it often becomes a repeated task.&lt;/p&gt;

&lt;p&gt;The real problem is not authentication itself. &lt;br&gt;
It is the lack of a clean structure and reusable foundation.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why authentication becomes messy
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;JWT logic mixed into controllers&lt;/li&gt;
&lt;li&gt;No clear separation of concerns&lt;/li&gt;
&lt;li&gt;Hard to maintain security config&lt;/li&gt;
&lt;li&gt;Different setup in every project&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What a production ready authentication system needs
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;User model with roles&lt;/li&gt;
&lt;li&gt;Token generation and validation&lt;/li&gt;
&lt;li&gt;Secure endpoints&lt;/li&gt;
&lt;li&gt;Clear separation of layers&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  How to implement JWT authentication (step by step)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Define user model
&lt;/h3&gt;

&lt;p&gt;Create user entity with roles and credentials.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Implement JWT
&lt;/h3&gt;

&lt;p&gt;Handle token generation and validation separately.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Configure security
&lt;/h3&gt;

&lt;p&gt;Setup filters and authentication providers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Secure endpoints
&lt;/h3&gt;

&lt;p&gt;Apply role-based access control.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Keep logic separate
&lt;/h3&gt;

&lt;p&gt;Do not mix auth with business logic.&lt;/p&gt;




&lt;h2&gt;
  
  
  JWT Authentication in Spring Boot Explained
&lt;/h2&gt;

&lt;p&gt;JWT (JSON Web Token) is a stateless authentication mechanism widely used in Spring Boot applications. &lt;/p&gt;

&lt;p&gt;It allows secure communication between client and server without storing session data.&lt;/p&gt;

&lt;p&gt;In a typical setup, the server generates a token after login. &lt;br&gt;
This token is sent with each request and validated before granting access.&lt;/p&gt;




&lt;h2&gt;
  
  
  Recommended authentication structure
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
src/
 ├── controller/
 ├── service/
 ├── security/
 ├── model/
 └── repository/

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

&lt;/div&gt;






&lt;h2&gt;
  
  
  Common mistakes to avoid
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Auth logic inside controllers&lt;/li&gt;
&lt;li&gt;Hardcoding secrets&lt;/li&gt;
&lt;li&gt;Skipping role checks&lt;/li&gt;
&lt;li&gt;Copy-paste implementations&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  How to avoid rebuilding authentication every time
&lt;/h2&gt;

&lt;p&gt;Treat authentication as a reusable module. Use a consistent structure so you can plug it into any project.&lt;/p&gt;

&lt;p&gt;Or instead of building this every time, you can start with a ready setup.&lt;/p&gt;




&lt;h2&gt;
  
  
  Don’t rebuild authentication again
&lt;/h2&gt;

&lt;p&gt;You can implement everything manually&lt;br&gt;&lt;br&gt;
or start with a ready setup.&lt;/p&gt;

&lt;p&gt;AuthKit-Lite includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JWT authentication&lt;/li&gt;
&lt;li&gt;role-based access control&lt;/li&gt;
&lt;li&gt;pre-built APIs&lt;/li&gt;
&lt;li&gt;clean project structure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 &lt;a href="https://buildbasekit.com/boilerplates/authkit-lite/" rel="noopener noreferrer"&gt;https://buildbasekit.com/boilerplates/authkit-lite/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Free and open source&lt;/p&gt;




&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;With the right structure, authentication becomes a one-time effort instead of repeated work.&lt;/p&gt;




&lt;h2&gt;
  
  
  Related articles
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://buildbasekit.com/blogs/jwt-mistakes-spring-boot/" rel="noopener noreferrer"&gt;JWT Mistakes in Spring Boot (Common Issues and Fixes)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Avoid common JWT mistakes in Spring Boot. Learn token validation, security issues, and how to structure authentication properly.&lt;/p&gt;




</description>
      <category>java</category>
      <category>springboot</category>
      <category>jwt</category>
      <category>backend</category>
    </item>
    <item>
      <title>How to Deploy a Production-Ready File Server on a VPS for Free</title>
      <dc:creator>buildbasekit</dc:creator>
      <pubDate>Sun, 22 Mar 2026 13:42:50 +0000</pubDate>
      <link>https://dev.to/buildbasekit/how-to-deploy-a-production-ready-file-server-on-a-vps-for-free-51ld</link>
      <guid>https://dev.to/buildbasekit/how-to-deploy-a-production-ready-file-server-on-a-vps-for-free-51ld</guid>
      <description>&lt;p&gt;This article walks through deploying a self-hosted file server on a VPS with a clean production setup: build the app, configure environment variables, run it with systemd, place Nginx in front, and secure the server with SSL and a firewall.&lt;/p&gt;




&lt;p&gt;Running your own file server is not only about saving on storage costs. It is also about control: you decide where files live, how uploads are handled, what gets exposed publicly, and how the server is secured.&lt;/p&gt;

&lt;p&gt;For this guide, we will use a Spring Boot based file server pattern similar to FiloraFS. The exact app name does not matter much here — the deployment flow is what matters. You can apply these steps to most Java backend file services.&lt;/p&gt;




&lt;h2&gt;
  
  
  What you will build
&lt;/h2&gt;

&lt;p&gt;By the end of this tutorial, you will have a file server that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;accepts file uploads through a backend API&lt;/li&gt;
&lt;li&gt;stores files on disk or in a mounted directory&lt;/li&gt;
&lt;li&gt;runs continuously on a VPS using systemd&lt;/li&gt;
&lt;li&gt;serves traffic through Nginx&lt;/li&gt;
&lt;li&gt;uses HTTPS with a free Let’s Encrypt certificate&lt;/li&gt;
&lt;li&gt;is protected by a basic firewall setup&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;A VPS with Ubuntu 22.04 or similar&lt;/li&gt;
&lt;li&gt;Java 21 installed on your local machine and server&lt;/li&gt;
&lt;li&gt;Git access to your project repository&lt;/li&gt;
&lt;li&gt;A domain name, if you want HTTPS with Nginx&lt;/li&gt;
&lt;li&gt;Your application jar built and ready to deploy with FiloraFS-Lite&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://www.buildbasekit.com/boilerplates/filora-fs-lite/" rel="noopener noreferrer"&gt;Grab source code from here →&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Prepare the application for production
&lt;/h2&gt;

&lt;p&gt;Start by making sure your app has a clean production configuration. Avoid hardcoding secrets or server-specific paths. Use environment variables or a separate production config file instead.&lt;/p&gt;

&lt;p&gt;A typical Spring Boot setup for a file server might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="c"&gt;# application-prod.properties
&lt;/span&gt;&lt;span class="py"&gt;spring.application.name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;filorafs&lt;/span&gt;
&lt;span class="py"&gt;server.port&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;8080&lt;/span&gt;

&lt;span class="py"&gt;storage.local.path&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/opt/filora/uploads&lt;/span&gt;
&lt;span class="py"&gt;spring.servlet.multipart.max-file-size&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;100MB&lt;/span&gt;
&lt;span class="py"&gt;spring.servlet.multipart.max-request-size&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;100MB&lt;/span&gt;

&lt;span class="py"&gt;spring.datasource.url&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;jdbc:mysql://localhost:3306/filorafs&lt;/span&gt;
&lt;span class="py"&gt;spring.datasource.username&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;filora_user&lt;/span&gt;
&lt;span class="py"&gt;spring.datasource.password&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;${DB_PASSWORD}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your project does not use a database, remove the &lt;code&gt;datasource&lt;/code&gt; block and keep only the storage and multipart settings.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Get a free VPS
&lt;/h2&gt;

&lt;p&gt;For a low-cost test deployment, you can use a free-tier VPS from a cloud provider that supports Linux instances. Any machine with public IP access, SSH access, and enough storage for your files will work.&lt;/p&gt;

&lt;p&gt;Once the VPS is ready, download the SSH key or note the login credentials so you can connect securely.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Connect to the server and install dependencies
&lt;/h2&gt;

&lt;p&gt;SSH into the server and install the tools required to run your backend.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh &lt;span class="nt"&gt;-i&lt;/span&gt; your-key.key ubuntu@your-vps-ip

&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;apt upgrade &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;openjdk-21-jdk nginx ufw &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you plan to build the application directly on the server, also install Git and Maven.&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;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;git maven &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. Upload the jar and create an app directory
&lt;/h2&gt;

&lt;p&gt;Create a clean folder for your application. Keeping the jar, logs, and environment file inside one directory makes maintenance easier.&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;sudo mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /opt/filora
&lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; &lt;span class="nv"&gt;$USER&lt;/span&gt;:&lt;span class="nv"&gt;$USER&lt;/span&gt; /opt/filora
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then copy your jar file into that directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;scp target/filorafs.jar ubuntu@your-vps-ip:/opt/filora/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. Add environment variables
&lt;/h2&gt;

&lt;p&gt;Put your secrets and server-specific values in a dedicated .env file. This keeps your service config simpler and avoids exposing values in the codebase.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="c"&gt;# /opt/filora/.env
&lt;/span&gt;&lt;span class="py"&gt;DB_PASSWORD&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;your_db_password&lt;/span&gt;
&lt;span class="py"&gt;SERVER_PORT&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;8080&lt;/span&gt;
&lt;span class="py"&gt;STORAGE_PATH&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/opt/filora/uploads&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  6. Run the app manually first
&lt;/h2&gt;

&lt;p&gt;Before creating a &lt;code&gt;systemd&lt;/code&gt; service, verify that the jar starts correctly in the terminal.&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;cd&lt;/span&gt; /opt/filora
&lt;span class="nb"&gt;source&lt;/span&gt; .env
java &lt;span class="nt"&gt;-jar&lt;/span&gt; filorafs.jar
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the application starts without errors, you are ready to make it persistent.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Create a &lt;code&gt;systemd&lt;/code&gt; service
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;systemd&lt;/code&gt; keeps the file server alive after reboot and restarts it automatically if the process crashes.&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;sudo &lt;/span&gt;nano /etc/systemd/system/filorafs.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Paste the following configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight systemd"&gt;&lt;code&gt;&lt;span class="k"&gt;[Unit]&lt;/span&gt;
&lt;span class="nt"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;FiloraFS File Server
&lt;span class="nt"&gt;After&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;network.target

&lt;span class="k"&gt;[Service]&lt;/span&gt;
&lt;span class="nt"&gt;User&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;ubuntu
&lt;span class="nt"&gt;WorkingDirectory&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;/opt/filora
&lt;span class="nt"&gt;EnvironmentFile&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;/opt/filora/.env
&lt;span class="nt"&gt;ExecStart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;/usr/bin/java -jar /opt/filora/filorafs.jar
&lt;span class="nt"&gt;Restart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;always
&lt;span class="nt"&gt;RestartSec&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;5

&lt;span class="k"&gt;[Install]&lt;/span&gt;
&lt;span class="nt"&gt;WantedBy&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;multi-user.target
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then enable and start it:&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;sudo &lt;/span&gt;systemctl daemon-reload
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;filorafs
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl start filorafs
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl status filorafs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  8. Put Nginx in front of the app
&lt;/h2&gt;

&lt;p&gt;Nginx is useful even for a simple backend. It gives you a stable public entry point, handles HTTPS termination, and lets you define upload size limits.&lt;/p&gt;

&lt;p&gt;Create a new site config:&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;sudo &lt;/span&gt;nano /etc/nginx/sites-available/filorafs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;yourdomain.com&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kn"&gt;client_max_body_size&lt;/span&gt; &lt;span class="mi"&gt;100M&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://localhost:8080&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Host&lt;/span&gt; &lt;span class="nv"&gt;$host&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Real-IP&lt;/span&gt; &lt;span class="nv"&gt;$remote_addr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Forwarded-For&lt;/span&gt; &lt;span class="nv"&gt;$proxy_add_x_forwarded_for&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Forwarded-Proto&lt;/span&gt; &lt;span class="nv"&gt;$scheme&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Enable the site and reload Nginx:&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;sudo ln&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; /etc/nginx/sites-available/filorafs /etc/nginx/sites-enabled/
&lt;span class="nb"&gt;sudo &lt;/span&gt;nginx &lt;span class="nt"&gt;-t&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl reload nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  9. Add HTTPS with Let’s Encrypt
&lt;/h2&gt;

&lt;p&gt;If your server is publicly accessible, SSL is not optional. Use Certbot to get a free certificate and let Nginx manage the HTTPS configuration.&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;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;certbot python3-certbot-nginx &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;certbot &lt;span class="nt"&gt;--nginx&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; yourdomain.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After setup, Certbot can renew certificates automatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  10. Lock down the firewall
&lt;/h2&gt;

&lt;p&gt;Use UFW to expose only the ports you actually need.&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;sudo &lt;/span&gt;ufw allow OpenSSH
&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw allow &lt;span class="s1"&gt;'Nginx Full'&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw &lt;span class="nb"&gt;enable&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your backend should never be reached directly, do not expose port 8080 publicly. Let Nginx talk to the app internally on localhost only.&lt;/p&gt;

&lt;h2&gt;
  
  
  11. Test everything
&lt;/h2&gt;

&lt;p&gt;Once the service is live, test upload, download, and delete flows from your browser or API client.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check app logs: &lt;code&gt;journalctl -u filorafs -f&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Check Nginx logs: &lt;code&gt;sudo tail -f /var/log/nginx/error.log&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Confirm HTTPS loads correctly&lt;/li&gt;
&lt;li&gt;Try a real file upload and verify the file appears in your storage directory&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Common mistakes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Using localhost in production config
&lt;/h3&gt;

&lt;p&gt;Inside a VPS, localhost is still fine for the app itself, but external clients should connect through your domain and Nginx.&lt;/p&gt;

&lt;h3&gt;
  
  
  Skipping file permissions
&lt;/h3&gt;

&lt;p&gt;Make sure the service user can write to the upload directory, otherwise file storage will fail even if the app starts.&lt;/p&gt;

&lt;h3&gt;
  
  
  Exposing the app port directly
&lt;/h3&gt;

&lt;p&gt;Let Nginx handle public traffic and keep the backend private whenever possible.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;A file server becomes much easier to manage once the deployment pattern is clean. Build the jar, move it to a VPS, run it with systemd, place Nginx in front, and secure the machine properly. That is the difference between a demo and a usable production setup.&lt;/p&gt;

&lt;p&gt;If you are using a starter like FiloraFS, this workflow gives you a reliable path from local development to a real deployed service without unnecessary complexity.&lt;/p&gt;




&lt;h3&gt;
  
  
  Original Article
&lt;/h3&gt;

&lt;p&gt;This post was originally published on BuildBaseKit.&lt;/p&gt;

&lt;p&gt;If you prefer reading on the main site or want updates, check it here:&lt;br&gt;&lt;br&gt;
&lt;a href="https://www.buildbasekit.com/blogs/how-to-deploy-a-production-ready-file-server-on-a-vps-for-free/" rel="noopener noreferrer"&gt;https://www.buildbasekit.com/blogs/how-to-deploy-a-production-ready-file-server-on-a-vps-for-free/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Published on March 15, 2026.&lt;/p&gt;

</description>
      <category>guide</category>
      <category>java</category>
      <category>deployment</category>
      <category>springboot</category>
    </item>
    <item>
      <title>How to Build and Deploy a Java Discord Bot Using Spring Boot</title>
      <dc:creator>buildbasekit</dc:creator>
      <pubDate>Sun, 22 Mar 2026 13:41:01 +0000</pubDate>
      <link>https://dev.to/buildbasekit/how-to-build-and-deploy-a-java-discord-bot-using-spring-boot-5d0b</link>
      <guid>https://dev.to/buildbasekit/how-to-build-and-deploy-a-java-discord-bot-using-spring-boot-5d0b</guid>
      <description>&lt;p&gt;This guide walks through setting up a Discord application, configuring your bot token, running the project locally, and deploying it on a VPS. The example uses Basely, a clean Java starter for Discord bot development.&lt;/p&gt;




&lt;p&gt;Discord bots are one of the easiest ways to automate community tasks, respond to events, and build useful tools for servers. For Java developers, the hard part is usually not the bot logic itself. It is the repetitive setup around authentication, command structure, project wiring, and deployment.&lt;/p&gt;

&lt;p&gt;That is where Basely fits in. It gives you a practical Spring Boot foundation for a Discord bot so you can focus on the actual features instead of rebuilding the same base every time.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Basely is for
&lt;/h2&gt;

&lt;p&gt;Basely is a Java Discord bot boilerplate designed for developers who want a clean starting point. It is useful for moderation bots, utility bots, reminder bots, community assistants, and learning projects built with JDA and Spring Boot.&lt;/p&gt;

&lt;p&gt;The idea is simple: instead of starting with an empty project and deciding everything from scratch, you begin with a structured codebase that already has a clear path for commands, events, services, and configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Before you start
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Java 21 or newer&lt;/li&gt;
&lt;li&gt;Maven installed locally, or the Maven wrapper included in the project&lt;/li&gt;
&lt;li&gt;A Discord account&lt;/li&gt;
&lt;li&gt;Access to the Discord Developer Portal&lt;/li&gt;
&lt;li&gt;A VPS with Java installed for production deployment&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  1. Create your Discord application
&lt;/h2&gt;

&lt;p&gt;Start by opening the &lt;a href="https://discord.com/developers/applications" rel="noopener noreferrer"&gt;Discord Developer Portal&lt;/a&gt;. From there, create a new application and give it a name that matches your bot.&lt;/p&gt;

&lt;p&gt;Once the application exists, open the Bot tab and click Add Bot. Discord will create the bot user attached to your application.&lt;/p&gt;

&lt;p&gt;Copy the bot token carefully. This token is the secret key your application uses to authenticate with Discord. Never commit it to GitHub and never paste it directly into public files.&lt;/p&gt;

&lt;h3&gt;
  
  
  Recommended settings in the Developer Portal
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Enable the bot user under the Bot tab&lt;/li&gt;
&lt;li&gt;Copy the token and store it securely&lt;/li&gt;
&lt;li&gt;Turn on only the privileged intents you actually need&lt;/li&gt;
&lt;li&gt;Keep the application name and avatar consistent with your project brand&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  2. Invite the bot to your server
&lt;/h2&gt;

&lt;p&gt;To test the bot locally, invite it to a Discord server where you have permission to manage integrations. Use the OAuth2 URL generator inside the Discord Developer Portal.&lt;/p&gt;

&lt;p&gt;Select the bot scope and, if your project uses slash commands, also select &lt;code&gt;applications.commands&lt;/code&gt;. Then choose the permissions your bot needs. For a test server, start with the minimum required permissions and expand later.&lt;/p&gt;

&lt;p&gt;After generating the invite link, open it in your browser and add the bot to the server.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Configure Basely locally
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.buildbasekit.com/boilerplates/basely/" rel="noopener noreferrer"&gt;Grab the project from here →&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then create your environment values. The exact variable names depend on your code, but a clean setup usually looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;spring.application.name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Basely&lt;/span&gt;
&lt;span class="py"&gt;server.port&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;8080&lt;/span&gt;

&lt;span class="py"&gt;discord.token&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;${DISCORD_BOT_TOKEN}&lt;/span&gt;
&lt;span class="py"&gt;discord.guild-id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;${DISCORD_GUILD_ID}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Adjust these names to match your implementation if your project uses a different configuration style.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Run the project locally
&lt;/h2&gt;

&lt;p&gt;Run project as Spring Boot App from your IDE.&lt;/p&gt;

&lt;p&gt;If the bot logs in successfully, you should see it connect to Discord and register the configured commands or listeners.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Prepare the VPS
&lt;/h2&gt;

&lt;p&gt;For production, use a VPS instead of your local machine. A VPS keeps the bot running continuously and gives you a stable environment for long-lived processes. Build jar from your project and upload it to VPS.&lt;/p&gt;

&lt;p&gt;Make sure docker compose is installed on your VPS.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose version
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If not then install docker + docker compose.&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;sudo install&lt;/span&gt; &lt;span class="nt"&gt;-m&lt;/span&gt; 0755 &lt;span class="nt"&gt;-d&lt;/span&gt; /etc/apt/keyrings

curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://download.docker.com/linux/ubuntu/gpg | &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;gpg &lt;span class="nt"&gt;--dearmor&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /etc/apt/keyrings/docker.gpg

&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;a+r /etc/apt/keyrings/docker.gpg

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="s2"&gt;"deb [arch=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;dpkg &lt;span class="nt"&gt;--print-architecture&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; signed-by=/etc/apt/keyrings/docker.gpg] &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
https://download.docker.com/linux/ubuntu &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; /etc/os-release &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$VERSION_CODENAME&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; stable"&lt;/span&gt; | &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;sudo tee&lt;/span&gt; /etc/apt/sources.list.d/docker.list &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null

&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update

&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; docker-ce docker-ce-cli containerd.io &lt;span class="se"&gt;\&lt;/span&gt;
docker-buildx-plugin docker-compose-plugin

&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl start docker
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;docker

&lt;span class="nb"&gt;sudo &lt;/span&gt;usermod &lt;span class="nt"&gt;-aG&lt;/span&gt; docker &lt;span class="nv"&gt;$USER&lt;/span&gt;
newgrp docker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  6. Run Bot with docker container
&lt;/h2&gt;

&lt;p&gt;On the VPS, create &lt;code&gt;docker-compose.yml&lt;/code&gt; file:&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;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3.9"&lt;/span&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;mysql&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;mysql:8.0&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;basely-mysql&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;

    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;MYSQL_ROOT_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rootpassword&lt;/span&gt;
      &lt;span class="na"&gt;MYSQL_DATABASE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;boiler_agent_discord_bot_db&lt;/span&gt;

    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;mysql_data:/var/lib/mysql&lt;/span&gt;

    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3306:3306"&lt;/span&gt;

&lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="na"&gt;basely&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;eclipse-temurin:21-jdk&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;basely-bot&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;

    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;mysql&lt;/span&gt;

    &lt;span class="na"&gt;working_dir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/app&lt;/span&gt;

    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./basely.jar:/app/basely.jar&lt;/span&gt;

    &lt;span class="na"&gt;env_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.env&lt;/span&gt;

    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;java"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-jar"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/app/basely.jar"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;SPRING_DATASOURCE_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;jdbc:mysql://mysql:3306/boiler_agent_discord_bot_db&lt;/span&gt;
      &lt;span class="na"&gt;SPRING_DATASOURCE_USERNAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;root&lt;/span&gt;
      &lt;span class="na"&gt;SPRING_DATASOURCE_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rootpassword&lt;/span&gt;

    &lt;span class="na"&gt;logging&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;max-size&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;10m"&lt;/span&gt;
        &lt;span class="na"&gt;max-file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3"&lt;/span&gt;

&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;mysql_data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and &lt;code&gt;.env&lt;/code&gt; file like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;DISCORD_TOKEN&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;your_discord_bot_token_here&lt;/span&gt;
&lt;span class="py"&gt;SPRING_PROFILES_ACTIVE&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;prod&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run basely with docker compose&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose run &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Common issues
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Bot stays offline
&lt;/h3&gt;

&lt;p&gt;Check whether the token is correct, whether the service is running, and whether the bot has permission to connect. A wrong environment variable name is a common cause.&lt;/p&gt;

&lt;h3&gt;
  
  
  Slash commands do not appear
&lt;/h3&gt;

&lt;p&gt;Make sure the bot was invited with the applications.commands scope and that your registration code is running at startup.&lt;/p&gt;

&lt;h3&gt;
  
  
  Service exits immediately
&lt;/h3&gt;

&lt;p&gt;Inspect the logs. Usually the issue is a missing environment file, incorrect jar path, or a startup exception from the application itself.&lt;/p&gt;




&lt;h3&gt;
  
  
  Final thoughts
&lt;/h3&gt;

&lt;p&gt;A Discord bot becomes much easier to ship when the base structure is already in place. Basely gives you that structure, while this guide shows how to connect the Discord application, keep the token secure, run the project locally, and move it to a VPS for production use.&lt;/p&gt;

&lt;p&gt;Once the foundation is working, you can start adding moderation tools, reminders, automation, dashboards, or any custom server workflow your project needs.&lt;/p&gt;




&lt;h3&gt;
  
  
  Original Article
&lt;/h3&gt;

&lt;p&gt;This post was originally published on BuildBaseKit.&lt;/p&gt;

&lt;p&gt;If you prefer reading on the main site or want updates, check it here:&lt;br&gt;&lt;br&gt;
&lt;a href="https://www.buildbasekit.com/blogs/how-to-build-and-deploy-a-java-discord-bot-using-spring-boot/" rel="noopener noreferrer"&gt;https://www.buildbasekit.com/blogs/how-to-build-and-deploy-a-java-discord-bot-using-spring-boot/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Published on March 15, 2026.&lt;/p&gt;

</description>
      <category>springboot</category>
      <category>java</category>
      <category>discord</category>
      <category>jda</category>
    </item>
  </channel>
</rss>
