{"id":13674190,"url":"https://github.com/m1k1o/blog","last_synced_at":"2025-04-06T07:11:40.248Z","repository":{"id":45382410,"uuid":"77480555","full_name":"m1k1o/blog","owner":"m1k1o","description":"Lightweight self-hosted facebook-styled PHP blog.","archived":false,"fork":false,"pushed_at":"2024-01-28T17:40:31.000Z","size":4224,"stargazers_count":299,"open_issues_count":9,"forks_count":31,"subscribers_count":13,"default_branch":"master","last_synced_at":"2024-05-02T06:08:33.702Z","etag":null,"topics":["blog","docker","facebook","lightweight","php","self-hosted"],"latest_commit_sha":null,"homepage":"https://blog.m1k1o.net","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/m1k1o.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null},"funding":{"github":["m1k1o"]}},"created_at":"2016-12-27T20:22:20.000Z","updated_at":"2024-05-02T06:08:33.703Z","dependencies_parsed_at":"2024-04-15T12:20:02.493Z","dependency_job_id":null,"html_url":"https://github.com/m1k1o/blog","commit_stats":null,"previous_names":[],"tags_count":13,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/m1k1o%2Fblog","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/m1k1o%2Fblog/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/m1k1o%2Fblog/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/m1k1o%2Fblog/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/m1k1o","download_url":"https://codeload.github.com/m1k1o/blog/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247445670,"owners_count":20939958,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["blog","docker","facebook","lightweight","php","self-hosted"],"created_at":"2024-08-02T11:00:42.882Z","updated_at":"2025-04-06T07:11:40.229Z","avatar_url":"https://github.com/m1k1o.png","language":"PHP","funding_links":["https://github.com/sponsors/m1k1o"],"categories":["Software","PHP","Blogging Platforms"],"sub_categories":["Blogging Platforms"],"readme":"# blog\nThis is a simple self-hosted, lightweight, singe-user PHP blog, where you can create your own Facebook-like feed. Give read access to other people, and you can share rich text with photos including highlighted code or links.\n\nIn this context lightweight means:\n* No npm dependency, there won't be an annoying 1GB `node_modules` directory.\n* No pipeline. What you see is pure code without a need to install it.\n* No overhead, essential features, simple usage.\n\n## Screenshots\n\u003cdetails\u003e\n\t\u003csummary\u003eLight theme\u003c/summary\u003e\n\n![screenshot](https://raw.githubusercontent.com/m1k1o/blog/master/static/screenshot-theme02-light.png)\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\t\u003csummary\u003eDark theme\u003c/summary\u003e\n\n![screenshot](https://raw.githubusercontent.com/m1k1o/blog/master/static/screenshot-theme02-dark.png)\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\t\u003csummary\u003eLegacy theme (compatible with older browsers)\u003c/summary\u003e\n\n![screenshot](https://raw.githubusercontent.com/m1k1o/blog/master/static/screenshot-theme01.png)\n\u003c/details\u003e\n\n## Zero configuration setup\nContainer will run without any initial configuration needed using SQLite as database provider. For better performance consider using MySQL.\n\n```sh\ndocker run -d -p 80:80 -v $PWD/data:/var/www/html/data m1k1o/blog:latest\n```\n\nYou can set environment variables, prefixed with `BLOG_` and uppercase. They can be found in `config.ini`.\n```sh\ndocker run -d \\\n  -p 80:80 \\\n  -e \"TZ=Europe/Vienna\" \\\n  -e \"BLOG_TITLE=Blog\" \\\n  -e \"BLOG_NAME=Max Musermann\" \\\n  -e \"BLOG_NICK=username\" \\\n  -e \"BLOG_PASS=password\" \\\n  -e \"BLOG_LANG=en\" \\\n  -v $PWD/data:/var/www/html/data \\\n  m1k1o/blog:latest\n```\n\nOr for docker-compose format, see [docker-compose.yml](docker-compose.yml).\n\n## Install standalone app using `docker-compose` with external database\nYou need to install [docker-compose](https://docs.docker.com/compose/install/).\n\n### MySQL\n```yaml\nversion: \"3\"\nservices:\n  webserver:\n    image: m1k1o/blog:latest\n    container_name: blog_apache\n    environment:\n      TZ: Europe/Vienna\n      BLOG_DB_CONNECTION: mysql\n      BLOG_MYSQL_HOST: mariadb\n      BLOG_MYSQL_PORT: 3306\n      BLOG_MYSQL_USER: blog\n      BLOG_MYSQL_PASS: blog # use secure password\n      BLOG_DB_NAME: blog\n    restart: unless-stopped\n    ports:\n      - ${HTTP_PORT-80}:80\n    volumes: \n      - ${DATA-./data}:/var/www/html/data\n  mariadb:\n    image: mariadb:10.1\n    container_name: blog_mariadb\n    environment:\n      MYSQL_USER: blog\n      MYSQL_PASSWORD: blog # use secure password\n      MYSQL_DATABASE: blog\n      MYSQL_ROOT_PASSWORD: root # use secure password\n    restart: unless-stopped\n    volumes:\n      - mariadb:/var/lib/mysql\n      - ./app/db/mysql:/docker-entrypoint-initdb.d:ro\nvolumes:\n  mariadb:\n```\n\n### Postgres\n```yaml\nversion: \"3\"\nservices:\n  webserver:\n    image: m1k1o/blog:latest\n    container_name: blog_apache\n    environment:\n      TZ: Europe/Vienna\n      BLOG_DB_CONNECTION: postgres\n      BLOG_POSTGRES_HOST: postgres\n      BLOG_POSTGRES_PORT: 5432\n      BLOG_POSTGRES_USER: blog\n      BLOG_POSTGRES_PASS: blog # use secure password\n      BLOG_DB_NAME: blog\n    restart: unless-stopped\n    ports:\n      - ${HTTP_PORT-80}:80\n    volumes: \n      - ${DATA-./data}:/var/www/html/data\n  postgres:\n    image: postgres:14\n    container_name: blog_postgres\n    environment:\n      POSTGRES_USER: blog\n      POSTGRES_PASSWORD: blog # use secure password\n      POSTGRES_DB: blog\n    restart: unless-stopped\n    volumes:\n      - postgres:/var/lib/postgresql/data\n      - ./app/db/postgres:/docker-entrypoint-initdb.d:ro\nvolumes:\n  postgres:\n```\n\n### Step 1: Run `docker-compose.yml`.\nSelect one of configurations above and save it to `docker-compose.yml`. Then run:\n```sh\ndocker-compose up -d\n```\n\nYou can specify these environment variables, otherwise the default ones will be used:\n* **HTTP_PORT=80** - where the blog will be accessible.\n* **DATA=./data** - directory to store the user data.\n\nThese environment variables can be stored in the `.env` file or passed to the command directly:\n```sh\nHTTP_PORT=3001 DATA=/home/user/blog docker-compose up -d\n```\n\n### Step 2: Create `data/` directory and download `config.ini` file.\nDownload default config file and copy to your new `./data/` directory.\n\n```sh\nmkdir data \u0026\u0026 cd data\nwget https://raw.githubusercontent.com/m1k1o/blog/master/config.ini\n```\n\nNow you can modify your config. Or you can set environment variables, in uppercase, starting with `BLOG_`, e.g. `BLOG_NAME: Max's blog`.\n\n### Correct permissions\nMake sure your `./data/` directory has correct permissions. Apache is running as a `www-data` user, which needs to have write access to the `./data/` directory (for uploading images).\n\n#### Prefered solution\nChange the directory owner to the `www-data` user:\n\n```sh\nchown 33:33 ./data/\n```\n\nAlternatively, add the `www-data` user to the user group that owns the `./data/` directory.\n\n#### Bad solution (but it works)\nSet `777` permission for your `./data/`, so everyone can read, write, and execute:\n\n```sh\nchmod 777 ./data/\n```\n\n**NOTICE:** You should not use `777`. You are giving access to anyone for this directory. Maybe to some attacker, who can run his exploit here.\n\n## Install\nIf you have decided that you don't want to use Docker, you can intall it manually.\n\n**Requirements:** Apache 2.0*, PHP 7.4, (MariaDB 10.1 or SQLite 3)\n\n**NOTICE:** If you would like to use Nginx or another web server, make sure that the sensitive data are not exposed to the public. Since `.htaccess` is protecting those files in Apache, that could not be the case in a different environment. Take care of:\n* **config.ini** - disallow access to all *.ini* files for the public.\n* **data/logs/\\_ANY_.log** - make sure no sensitive information are located in *.log*.\n\n### Database Schema\nYou can find database schema in `./app/db` folder.\n\n### Debug mode\nTo check if your server is set up correctly, turn on a debug mode (in config add `debug = true`) to see the details. In the debug mode, an error may be shown if you are missing some **PHP extensions** needed to be installed on your server.\n\n## Config file\n**DO NOT** edit `./config.ini` file. If you wish to modify the config, simply make a copy to the `./data/config.ini` directory and edit it there.\n\n**But, why?** If there is any change in config file (most likely adding a new feature), you will have problems with merging a new version. Also, if you would fork this repository, you might accidentally push your secrets to the git. We don't want that to happen. Content of the `/data` directory is ignored by the git, so none of your pictures or personal data should ever be published to git.\n\n# Features\n\n* Dark mode, retina ready, legacy theme available.\n* Use BBcode in texts.\n* Make posts available for **everyone**, **only you** or just for **friends**.\n* Extra fields in post: **Feeling**, **With** and **At**.\n* Hide posts from timeline so they are visible only when you need them to be.\n* All pasted links will get preview with page title, description and image (can be configured proxy).\n* Upload images using button *(for mobile)*.\n* Upload images using drag \u0026 drop *(drop it into textarea)*.\n* Upload images using CTRL + V *(paste it into textarea)*. \n* Highlight code in post using `[code]..your code..[/code]`.\n* Highlight your goal using `[goal]Text of your goal.[/goal]`.\n* Use tags in posts (allowed characters `A-Za-z0-9-_` terminated by space or EOL): `#song`.\n* Sort posts in reverse order (oldest first): `http://blog/#sort=reverse`.\n* Filter posts by hashtags: `http://blog/#tag=songs`.\n* Filter posts by location in url using: `http://blog/#loc=Vienna`.\n* Display posts from chosen date using (format YYYY-MM-DD or YYY-MM): `http://blog/#from=2017-06`.\n* Display posts to chosen date using (format YYYY-MM-DD or YYY-MM): `http://blog/#to=2017-06`.\n* Combine parameters in url using `\u0026`, e.g. show posts between dates: `http://blog/#from=2017-06\u0026to=2017-08`.\n\n## Access control\n\nThis blog is using Mandatory Access Control (MAC), with 3 types of access levels:\n\n* **Private** posts are visible only to your single account specified in `nick` and `pass`.\n* You can specify group of your **friends** and share posts only for them.\n* **Public** posts are visible to everyone, without login.\n\nIn `docker-compose.yml` file, specify your credentials and friends like this:\n\n```yml\nversion: \"3\"\nservices:\n  blog:\n    image: m1k1o/blog:latest\n    restart: unless-stopped\n    environment:\n        TZ: Europe/Vienna\n        BLOG_NICK: admin_username\n        BLOG_PASS: admin_password\n        BLOG_FRIENDS: |\n          jane:mysecretpass\n          thomas:anotherpass\n    ports:\n      - 80:80\n    volumes:\n      - ./data:/var/www/html/data\n```\n\nYou can specify your credentials and friends in your `config.ini` file e.g.:\n\n```ini\n[admin]\nforce_login = true\nnick = admin_username\npass = admin_password\n\n[friends] \nfriends[jane] = mysecretpass\nfriends[thomas] = anotherpass\n```\n\n## Localisation\nTimezone can be set in config or, for docker users, `TZ` environment variable is supported. List of timezones can be found [here](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones).\n\n### Language support\nFeel free to create new PR and add a new language. Specify language in config or in url: `http://blog/?hl=sk`.\n\n* en - 🇬🇧 English\n* de - 🇩🇪 German\n* sk - 🇸🇰 Slovak\n* fr - 🇫🇷 French (thanks @Phundrak)\n* cz - 🇨🇿 Czech (thanks @djfinch)\n* bs - 🇧🇦 Bosnian (thanks @hajro92)\n* es - 🇪🇸 Spanish (thanks @ManuLinares)\n* ru - 🇷🇺 Russian (thanks @ozzyst)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fm1k1o%2Fblog","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fm1k1o%2Fblog","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fm1k1o%2Fblog/lists"}