<?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: sam3maker</title>
    <description>The latest articles on DEV Community by sam3maker (@sam3maker).</description>
    <link>https://dev.to/sam3maker</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%2F3926805%2F472ab705-72a2-4ae3-b8a4-405381dedc83.png</url>
      <title>DEV Community: sam3maker</title>
      <link>https://dev.to/sam3maker</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sam3maker"/>
    <language>en</language>
    <item>
      <title>I Built a Free Open-Source Blog Platform with Flask and TiDB</title>
      <dc:creator>sam3maker</dc:creator>
      <pubDate>Tue, 12 May 2026 10:20:40 +0000</pubDate>
      <link>https://dev.to/sam3maker/i-built-a-free-open-source-blog-platform-with-flask-and-tidb-4ea4</link>
      <guid>https://dev.to/sam3maker/i-built-a-free-open-source-blog-platform-with-flask-and-tidb-4ea4</guid>
      <description>&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;I wanted a blog platform that was:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Free&lt;/strong&gt; to host (no monthly bills)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lightweight&lt;/strong&gt; (not a 500MB WordPress install)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open source&lt;/strong&gt; (full control over my content)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Modern&lt;/strong&gt; (Markdown editor, dark mode, i18n)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Everything I found was either too heavy, too expensive, or too restrictive. So I built &lt;strong&gt;OpenBlog&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is OpenBlog?
&lt;/h2&gt;

&lt;p&gt;OpenBlog is a fully open-source blogging platform built with &lt;strong&gt;Python/Flask&lt;/strong&gt; and &lt;strong&gt;TiDB&lt;/strong&gt; (MySQL-compatible serverless database). Deploy it on &lt;strong&gt;Hugging Face Spaces for free&lt;/strong&gt; — zero cost, zero credit card.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Live Demo:&lt;/strong&gt; &lt;a href="https://sam3maker-openblog.hf.space" rel="noopener noreferrer"&gt;sam3maker-openblog.hf.space&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Source Code:&lt;/strong&gt; &lt;a href="https://github.com/sam3maker/openblog" rel="noopener noreferrer"&gt;github.com/sam3maker/openblog&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Key Features
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Dual Editor
&lt;/h3&gt;

&lt;p&gt;Write in &lt;strong&gt;Markdown&lt;/strong&gt; with live preview, or switch to &lt;strong&gt;Rich Text&lt;/strong&gt; (Quill.js) — your choice.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Hello World&lt;/span&gt;
This is &lt;span class="gs"&gt;**Markdown**&lt;/span&gt; with live preview.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Community Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Likes &amp;amp; Bookmarks&lt;/li&gt;
&lt;li&gt;Nested comments with replies&lt;/li&gt;
&lt;li&gt;Follow system &amp;amp; activity timeline&lt;/li&gt;
&lt;li&gt;User profiles with avatars&lt;/li&gt;
&lt;li&gt;User blocking&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Admin Dashboard
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Content moderation &amp;amp; report system&lt;/li&gt;
&lt;li&gt;User management (activate/deactivate/ban by IP)&lt;/li&gt;
&lt;li&gt;Site-wide configuration&lt;/li&gt;
&lt;li&gt;Statistics &amp;amp; analytics&lt;/li&gt;
&lt;li&gt;Audit logging&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Multi-language (i18n)
&lt;/h3&gt;

&lt;p&gt;7 languages out of the box: Chinese, English, Japanese, Korean, French, German, Spanish&lt;/p&gt;

&lt;h3&gt;
  
  
  Security
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;2FA with TOTP &amp;amp; recovery codes&lt;/li&gt;
&lt;li&gt;CSRF protection on all forms&lt;/li&gt;
&lt;li&gt;HTML sanitization (nh3)&lt;/li&gt;
&lt;li&gt;Rate limiting on auth &amp;amp; API&lt;/li&gt;
&lt;li&gt;IP banning with auto-expiry&lt;/li&gt;
&lt;li&gt;OAuth state signing (HMAC)&lt;/li&gt;
&lt;li&gt;Account lockout after 5 failed logins&lt;/li&gt;
&lt;li&gt;Security headers (CSP, HSTS, X-Content-Type-Options)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  More Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Dark/Light theme toggle&lt;/li&gt;
&lt;li&gt;Full-text search&lt;/li&gt;
&lt;li&gt;Image upload with auto-compression (PNG, JPEG, WEBP, GIF preserved)&lt;/li&gt;
&lt;li&gt;Scheduled publishing with background scheduler&lt;/li&gt;
&lt;li&gt;Article version history&lt;/li&gt;
&lt;li&gt;Tag &amp;amp; category system&lt;/li&gt;
&lt;li&gt;GitHub &amp;amp; Hugging Face OAuth login&lt;/li&gt;
&lt;li&gt;CJK-friendly URL slugs&lt;/li&gt;
&lt;li&gt;RSS feed &amp;amp; sitemap &amp;amp; robots.txt&lt;/li&gt;
&lt;li&gt;SEO-ready meta tags&lt;/li&gt;
&lt;li&gt;Responsive design (Bootstrap 5)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Tech Stack
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Technology&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Backend&lt;/td&gt;
&lt;td&gt;Python 3.10+ / Flask / SQLAlchemy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Frontend&lt;/td&gt;
&lt;td&gt;HTML5 / CSS3 / JavaScript / Bootstrap 5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Database&lt;/td&gt;
&lt;td&gt;TiDB (MySQL-compatible, serverless)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Editor&lt;/td&gt;
&lt;td&gt;Marked.js + Quill.js&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hosting&lt;/td&gt;
&lt;td&gt;Hugging Face Spaces (Docker)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cache&lt;/td&gt;
&lt;td&gt;FileSystemCache (cross-worker)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Auth&lt;/td&gt;
&lt;td&gt;Flask-Login + OAuth + 2FA&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Deploy in 5 Minutes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Option 1: Hugging Face Spaces (Free, Recommended)
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Fork&lt;/strong&gt; the repo on GitHub&lt;/li&gt;
&lt;li&gt;Go to &lt;a href="https://huggingface.co/spaces" rel="noopener noreferrer"&gt;Hugging Face Spaces&lt;/a&gt; → Create new Space → Select &lt;strong&gt;Docker&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Link your GitHub repo&lt;/li&gt;
&lt;li&gt;Add environment variables in Space Settings:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;   &lt;span class="py"&gt;MYSQL_HOST&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;your-tidb-host&lt;/span&gt;
   &lt;span class="py"&gt;MYSQL_PORT&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;4000&lt;/span&gt;
   &lt;span class="py"&gt;MYSQL_USER&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;your-username&lt;/span&gt;
   &lt;span class="py"&gt;MYSQL_PASSWORD&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;your-password&lt;/span&gt;
   &lt;span class="py"&gt;MYSQL_DATABASE&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;openblog&lt;/span&gt;
   &lt;span class="py"&gt;SECRET_KEY&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;your-secret-key&lt;/span&gt;
   &lt;span class="py"&gt;MYSQL_SSL_CA_CONTENT&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;your SSL cert content&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Done! Your blog is live.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Option 2: Local Development
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/sam3maker/openblog.git
&lt;span class="nb"&gt;cd &lt;/span&gt;openblog
&lt;span class="nb"&gt;cp&lt;/span&gt; .env.example .env
&lt;span class="c"&gt;# Edit .env with your TiDB Cloud credentials&lt;/span&gt;
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
python run.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Visit &lt;code&gt;http://localhost:5000&lt;/code&gt; — the admin account is created automatically.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TiDB Cloud free tier&lt;/strong&gt; gives you 5GB storage, 50M request units/month — more than enough for a blog.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Project Structure
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;openblog/
├── app/
│   ├── config.py           # Configuration
│   ├── models.py           # 13 database tables
│   ├── i18n/               # 7 languages
│   ├── routes/
│   │   ├── auth.py         # Auth, OAuth, 2FA
│   │   ├── blog.py         # Blog CRUD, RSS, scheduler
│   │   ├── community.py    # Likes, bookmarks, comments, follows
│   │   ├── admin.py        # Admin panel
│   │   ├── user.py         # Profiles, settings
│   │   └── api.py          # REST API
│   └── utils.py            # Helpers &amp;amp; utilities
├── templates/              # Jinja2 templates
├── static/                 # CSS, JS, uploads
└── run.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  API Endpoints
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Endpoint&lt;/th&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/api/search?q=&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;GET&lt;/td&gt;
&lt;td&gt;Global search&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/api/tags&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;GET&lt;/td&gt;
&lt;td&gt;Tag list&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/api/categories&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;GET&lt;/td&gt;
&lt;td&gt;Category list&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/api/stats&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;GET&lt;/td&gt;
&lt;td&gt;Site statistics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/api/upload&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;POST&lt;/td&gt;
&lt;td&gt;Image upload&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/feed&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;GET&lt;/td&gt;
&lt;td&gt;RSS feed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/sitemap.xml&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;GET&lt;/td&gt;
&lt;td&gt;Sitemap&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Why TiDB?
&lt;/h2&gt;

&lt;p&gt;Most blog platforms use SQLite or PostgreSQL. I chose &lt;strong&gt;TiDB&lt;/strong&gt; because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Serverless&lt;/strong&gt; — no database server to manage&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MySQL-compatible&lt;/strong&gt; — uses standard SQLAlchemy&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Free tier&lt;/strong&gt; — generous limits for personal blogs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalable&lt;/strong&gt; — grows with your traffic&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Plugin system for community extensions&lt;/li&gt;
&lt;li&gt;API documentation (Swagger/OpenAPI)&lt;/li&gt;
&lt;li&gt;Docker Compose for one-command local dev&lt;/li&gt;
&lt;li&gt;Database backup &amp;amp; export&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Contributing
&lt;/h2&gt;

&lt;p&gt;OpenBlog is open source under the &lt;strong&gt;MIT License&lt;/strong&gt;. Issues and Pull Requests are welcome!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/sam3maker/openblog" rel="noopener noreferrer"&gt;github.com/sam3maker/openblog&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you find this project useful, please give it a ⭐ on GitHub — it helps a lot!&lt;/p&gt;

</description>
      <category>flask</category>
      <category>opensource</category>
      <category>python</category>
      <category>website</category>
    </item>
  </channel>
</rss>
