# Blogin: a Flask blog, photo album and tools box in one personal site

> Blogin is a Flask and Bootstrap 4 personal site with a front end for posts, a photo album, a comment system, several small online tools and an admin back end that reads server logs and resource usage. It is a Chinese developer project with a Jinja based front end, MySQL for storage and Redis for caching, and its deployment instructions assume a Chinese cloud host.

**weijiang1994/Blogin** — A blog system depend on flask.使用flask框架开发的个人博客系统.

- Repository: https://github.com/weijiang1994/Blogin
- Website: https://2dogz.cn
- Stars: 526 · Forks: 105
- Language: Python
- License: GPL-3.0
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/weijiang1994-blogin

## One Flask application, front end and back end

Blogin is described in the repository as a personal blog system developed with the Flask framework, and the readme puts it more precisely: a personal blog site built on Flask and Bootstrap 4, with a front end and a back end.

That two part structure is the shape of the project, and the readme enumerates both halves in detail. The front end is the public site: a personal blog whose home page shows a short introduction to each post, blog categories, comments and social network sharing, and blog archiving. Alongside that is a personal photo album with tag support, comment likes and social account sharing. There is a comment system with commenting, deleting and reporting, plus threaded replies to comments. There is a user profile area for site users, showing personal activity, unread message notifications when someone replies to your comment, profile and password editing, and a login log that records the actual location of each login.

Then there is a group of small online tools listed as front end features: an online word cloud generator, a multi platform translation tool, a Tang and Song poetry lookup, an online OCR tool, and an online IP real address lookup tool.

The poetry and the motivational quote collection are also their own sections, each offering a random poem or a random quote, and each providing an API endpoint for fetching one.

The back end covers content management, interaction management and server management. Content management handles blog, album, poetry and quote creation and editing, with deletion that hides the item on the front end rather than removing it, and personal plans that can be created, edited and completed. Interaction management covers viewing and hiding comments, viewing users, disabling a user so they cannot log in, and setting permissions between administrator and user. Server management is the unusual one: it reports CPU usage, memory usage and network traffic, and it shows nginx logs, application error logs and nginx error logs.

Other front end features include personal plan tracking, a contribution heat map for the last three months, and a record of the site's update milestones.

The demo site is at 2dogz.cn, and the readme links an English version of the document, README-EN.md, which is in the repository root alongside the Chinese one.

## Installing the stack: MySQL, Redis and a database named blog

The installation path is fully spelled out, and it starts with the two services the application expects.

Linux and macOS users can run the install.sh script in the repository root, which the readme presents as a one command install.

```bash
      cd Blogin
      ./install.sh
```

Otherwise MySQL is installed first.

```shell
    sudo apt-get install mysql
```

Then Redis, which is installed from its own site or with the package manager, and started as a service.

```shell
   sudo service redis-server start
```

The database itself is created by hand through the MySQL console, and the readme is explicit about the character set and collation.

```mysql
   create database blog CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
   SHOW DATABASES ;
```

If the console output contains the blog database, the creation succeeded.

The choice of utf8mb4 with utf8mb4_general_ci is worth a note, because it is the one database decision that matters for a blog that will eventually receive an emoji or a character outside the basic multilingual plane. utf8mb4 is the only MySQL charset that can store those, and utf8mb4_general_ci is the collation that goes with it for a general purpose site that still wants case insensitive comparison.

After the services, the code is cloned and the environment is configured. The readme is unusually clear about why the .env file exists: many of the project's attributes are private or confidential, so their values are stored in .env and that file is excluded when the code is uploaded.

The .env keys cover more than a database password. There are mail server settings for a QQ or 163 mailbox with a verification code rather than the account password, a SECRET_KEY for the project, the database user and password, Baidu Translate credentials for the translation tool, and GitHub and Gitee OAuth client identifiers and secrets for third party sign in.

A separate .flaskenv file sets FLASK_APP and FLASK_ENV, which tells Flask which module to load and that this is the development environment.

## The virtual environment, the pinned dependencies and the init commands

The Python side is a standard virtual environment workflow.

```shell
     cd Blogin
     python3 -m venv venv
```

And on Unix it is activated with the usual source command, with a separate Windows form in the readme.

```shell
   source venv/bin/activate
```

Dependencies are then installed from requirements.txt, and the readme points at a pip index mirror rather than PyPI directly.

```shell
     cd Blogin
     pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
```

That mirror choice is a regional convenience and it has a consequence: anyone outside that network is relying on the fallback behaviour of pip, and a production deployment would normally be pointed at PyPI instead.

The database is initialised through two Flask CLI commands.

```bash
   cd Blogin
   flask initdb
   flask admin
```

The first creates the tables and the second creates the administrator account, so the order matters, since the second presumably writes a user row into tables the first created. The run command is then just flask run, and the readme points you at http://127.0.0.1:5000 to see the pages.

The dependency list is where the age of the project shows. Every version is pinned with an equals sign, and they are pinned to releases from 2020. Flask is 1.1.2, Werkzeug 1.0.1, SQLAlchemy 1.3.19, Jinja2 2.11.2, Flask-SQLAlchemy 2.4.4, Flask-Migrate 2.5.3 and Alembic 1.4.3. redis is 3.5.3 and PyMySQL 0.10.1. There are also two packages added after the main block, flask-cors 5.0.0 and flask-jwt-extended 3.25.0.

There are about seventy packages in total, and a few of them explain the feature list directly. jieba is the Chinese word segmenter behind the word cloud generator, wordcloud and kiwisolver draw it, imageio and lxml and beautifulsoup4 handle the scraping and OCR side, Flask-CKEditor and Flask-Share and Flask-Moment cover the rich text editor, social sharing and date rendering, Flask-Babel and babel.cfg and the translations/ directory do the internationalisation, and Flask-Avatars handles the gravatar style avatars for the user profiles. Whoosh and flask-whooshee and the whooshee/ directory are the search index, psutil is what reads the CPU and memory figures, APScheduler and Flask-APScheduler and scheduler.lock are the scheduled jobs, feedgen is the RSS output, and bleach and visitor sanitise user submitted HTML.

The Python badge in the readme header names 3.6, 3.7, 3.8 and 3.9, which is a further hint at the vintage.

## Docker deployment, with the source tree mounted into the container

The Docker route is the one the project seems to expect for a real deployment, and the compose file is where the architecture is clearest.

The build command is one line.

```bash
sudo docker-compose up --build d
```

The compose file defines three services. MySQL runs from the mysql:8.0.23 image with the root password, database name, user and password all taken from the environment file, a volume at /data/services/blogin/database, and a healthcheck that runs mysqladmin ping against localhost every minute with three retries. Redis runs from the redis:latest image with a healthcheck that uses redis-cli to increment a key named ping, and the application service builds from the local Dockerfile, publishes port 8000, and depends on both.

The application service environment sets REDIS_HOST and REDIS_PORT, and its volumes mount the current directory at /app and a host directory at /var/logs. That second volume is what makes the server management panel work: the admin back end reads nginx and application error logs, and it can only do that if the log directory is shared with the container.

The compose file uses a named network, blogin-network, and the timezone and locale are pinned to Asia/Shanghai with C.UTF-8 in both the database and the Redis service.

Once the containers are up, the database is initialised from inside the running container.

```bash
sudo docker exec blogin /bin/bash -c "flask initdb"
sudo docker exec blogin /bin/bash -c "flask admin-docker"
```

The admin-docker command is separate from the local admin command, and the browser then reaches the site at http://127.0.0.1:8000.

The Dockerfile behind the application service is worth reading before you build it. It starts from ubuntu:20.04, installs python3, gcc, python3-dev, python3-pip and tzdata, installs the requirements file through the same pip mirror, then adds gunicorn 20.0.4 and cryptography on a second pip line, and links the timezone into /etc/localtime. The apt sources are rewritten to a Huawei Cloud mirror in the same RUN layer, and the final command runs gunicorn against wsgi:app with a gunicorn_conf.py configuration.

Two details in that Dockerfile deserve a second look if you are deploying this anywhere real. The apt mirror rewrite and the pip mirror both point at Chinese mirrors, which is a latency and availability decision rather than a technical one, and the cryptography package is installed separately with a pinned gunicorn rather than being left to the resolver.

## Where Blogin is the wrong tool

There are four reasons to look elsewhere, and three of them are about age rather than design.

The first is that this is a personal site system, not a publishing platform. The features are the features of one person's site: a photo album with tags, personal plans, a contribution heat map, poetry and quote collections, a set of small tools, a milestone log and a friend links page. There is no multi author support, no editorial workflow, no roles beyond administrator and user, and no content model for anything other than what one person publishes. If you need a team blog or a company news site, this is the wrong shape of application, and adding multi tenancy to it would be a rewrite rather than a configuration change.

The second is the dependency vintage, and it is the practical blocker. Flask 1.1.2 and Werkzeug 1.0.1 and SQLAlchemy 1.3.19 were current in 2020. Those versions predate a good deal of change in the Flask ecosystem, they predate the Python versions most people run now, and pinning them with equals signs means pip will not resolve around a conflict. The Python badge names 3.6 through 3.9, and the Dockerfile is ubuntu:20.04, which has itself moved on. None of this makes the project unusable, but it means you are running software built for a runtime environment from four years ago.

The third is the server management panel. Reading nginx logs and application error logs from the host filesystem, and displaying CPU, memory and network usage inside the admin area, is a feature that needs the log directory mounted and the right file permissions. In a containerised deployment that means the application can read files it arguably should not, and the panel breaks the moment the logs go somewhere else, which is to say the moment you move to a log aggregator. It is a convenience that couples the application to the host.

The fourth is the security surface of a project like this in general. The readme shows the .env file with the mail password, the secret key, the database password and the OAuth client secrets, and the .env is excluded from version control, which is the right arrangement. But the feature set includes a rich text editor with server side HTML sanitising, a comment system with user accounts and reporting, third party OAuth sign in, a JWT extension, an OCR tool that accepts uploads, and an IP lookup that makes an outbound request. That is a fair amount of untrusted input reaching a 2020 era Flask stack, and the readme does not discuss a security review.

On the alternative, the difference in approach against a modern static site generator is where the content lives. A static site generator turns Markdown into HTML at build time, and the result is a set of files that any web server can serve, with no application server, no database and no attack surface beyond the static host. Blogin keeps a database, a session layer, a caching layer and a running Python process, and in exchange it gives you the things a static site does not do easily: comments with accounts and notifications, a photo album with tags, per user profiles, and live data.

So the trade is static simplicity against interactive features. If you need the comments and the album, a dynamic application is the only way to get them and Blogin already has them. If you do not, a static generator gives you a site that is faster, cheaper to host and much harder to break.

## Conclusion

Blogin suits a developer who wants a complete personal site rather than a bare blog engine, because the album, the comment system, the tools and the admin panels come as one application, and it does not suit a team that needs a maintained content platform today, since the dependency set is pinned to 2020 releases. Before you deploy it, check whether the pinned Flask and SQLAlchemy versions still install on your Python, since requirements.txt names Flask 1.1.2 and SQLAlchemy 1.3.19, and decide how you want the server status panel handled, because it reads nginx and application logs from the host filesystem.

## FAQ

### What is Blogin?

Blogin is a personal blog system built with the Flask Python web framework and Bootstrap 4, with a public front end for posts, a photo album, comments, several small online tools, and an admin back end for content, users and server status. A live demo runs at 2dogz.cn.

### How do I install Blogin?

Linux and macOS users can run the install.sh script in the repository root. Otherwise install MySQL and Redis, create a database named blog with utf8mb4, clone the repository, create a .env file and a .flaskenv file, make a virtual environment, install the requirements, then run flask initdb and flask admin before flask run.

### What database and cache does Blogin use?

MySQL or MariaDB for the data, with a database created as utf8mb4 with the utf8mb4_general_ci collation, and Redis for caching. The Docker Compose file runs mysql:8.0.23 and redis:latest alongside the application service.

### How do I deploy Blogin with Docker?

Configure the .env file, run sudo docker-compose up --build d, then initialise the database inside the running container with flask initdb and create the default administrator with flask admin-docker. The site is then reachable on port 8000.

### What licence is Blogin released under?

The repository is recorded as GPL-3.0, with the LICENSE file at the repository root. The readme also links an English version of the documentation in README-EN.md.

## Sources

- [Issues](https://github.com/weijiang1994/Blogin/issues)
- [License: GPL-3.0](https://github.com/weijiang1994/Blogin/blob/master/LICENSE)
- [Project website](https://2dogz.cn)
- [README](https://github.com/weijiang1994/Blogin/blob/master/README.md)
- [weijiang1994/Blogin on GitHub](https://github.com/weijiang1994/Blogin)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/weijiang1994-blogin
