LinkX is an open‑source social media planner that you can self‑host with Docker Compose. It gives you a unified dashboard to draft, schedule, and publish posts across platforms while you retain full control of your data and infrastructure.
- ⚡ FastAPI backend API.
- 🧰 SQLModel ORM over PostgreSQL.
- 🔍 Pydantic for data validation and settings.
- 💾 PostgreSQL as the database.
- 🚀 React frontend.
- TypeScript, hooks, Vite, and modern tooling.
- 🎨 Tailwind CSS + shadcn/ui components.
- 🧪 Playwright for end‑to‑end testing.
- 🦇 Built‑in dark mode support.
- 🐋 Docker Compose for local and production deployments.
- 🔒 Secure password hashing and JWT authentication.
- 📫 Email‑based password recovery with Mailcatcher in development.
- ✅ Tests with Pytest.
- 📞 Traefik reverse proxy / load balancer.
- 🚢 Deployment docs for running LinkX as your own self‑hosted social scheduler.
- 🏭 CI/CD workflows using GitHub Actions.
- LinkedIn member posting (self‑hosted):
- Connect a personal LinkedIn account and publish or delete posts directly from LinkX.
- Uses OAuth 2.0 with scopes
openid profile email w_member_social. - Tokens are stored server‑side only in your deployment; no secrets are exposed to the browser.
- See
docs/LINKEDIN_SETUP.mdfor step‑by‑step configuration.
- Planned:
- X (Twitter) integration.
- Richer media workflows and cross‑posting, tracked in
docs/specs/SOCIAL_MEDIA_INTEGRATION.mdand related specs.
You can just fork or clone this repository and use it as is.
✨ It just works. ✨
If you want to have a private repository, GitHub won't allow you to simply fork it as it doesn't allow changing the visibility of forks.
But you can do the following:
- Create a new GitHub repo, for example
my-full-stack. - Clone this repository manually, set the name with the name of the project you want to use, for example
my-full-stack:
git clone git@github.com:fastapi/full-stack-fastapi-template.git my-full-stack- Enter into the new directory:
cd my-full-stack- Set the new origin to your new repository, copy it from the GitHub interface, for example:
git remote set-url origin git@github.com:octocat/my-full-stack.git- Add this repo as another "remote" to allow you to get updates later:
git remote add upstream git@github.com:fastapi/full-stack-fastapi-template.git- Push the code to your new repository:
git push -u origin masterAfter cloning the repository, and after doing changes, you might want to get the latest changes from this original template.
- Make sure you added the original repository as a remote, you can check it with:
git remote -v
origin git@github.com:octocat/my-full-stack.git (fetch)
origin git@github.com:octocat/my-full-stack.git (push)
upstream git@github.com:fastapi/full-stack-fastapi-template.git (fetch)
upstream git@github.com:fastapi/full-stack-fastapi-template.git (push)- Pull the latest changes without merging:
git pull --no-commit upstream masterThis will download the latest changes from this template without committing them, that way you can check everything is right before committing.
-
If there are conflicts, solve them in your editor.
-
Once you are done, commit the changes:
git merge --continueYou can then update configs in the .env files to customize your configurations.
Before deploying it, make sure you change at least the values for:
SECRET_KEYFIRST_SUPERUSER_PASSWORDPOSTGRES_PASSWORD
You can (and should) pass these as environment variables from secrets.
Read the deployment.md docs for more details.
Some environment variables in the .env file have a default value of changethis.
You have to change them with a secret key, to generate secret keys you can run the following command:
python -c "import secrets; print(secrets.token_urlsafe(32))"Copy the content and use that as password / secret key. And run that again to generate another secure key.
This repository also supports generating a new project using Copier.
It will copy all the files, ask you configuration questions, and update the .env files with your answers.
You can install Copier with:
pip install copierOr better, if you have pipx, you can run it with:
pipx install copierNote: If you have pipx, installing copier is optional, you could run it directly.
Decide a name for your new project's directory, you will use it below. For example, my-awesome-project.
Go to the directory that will be the parent of your project, and run the command with your project's name:
copier copy https://github.com/fastapi/full-stack-fastapi-template my-awesome-project --trustIf you have pipx and you didn't install copier, you can run it directly:
pipx run copier copy https://github.com/fastapi/full-stack-fastapi-template my-awesome-project --trustNote the --trust option is necessary to be able to execute a post-creation script that updates your .env files.
Copier will ask you for some data, you might want to have at hand before generating the project.
But don't worry, you can just update any of that in the .env files afterwards.
The input variables, with their default values (some auto generated) are:
project_name: (default:"FastAPI Project") The name of the project, shown to API users (in .env).stack_name: (default:"fastapi-project") The name of the stack used for Docker Compose labels and project name (no spaces, no periods) (in .env).secret_key: (default:"changethis") The secret key for the project, used for security, stored in .env, you can generate one with the method above.first_superuser: (default:"admin@example.com") The email of the first superuser (in .env).first_superuser_password: (default:"changethis") The password of the first superuser (in .env).smtp_host: (default: "") The SMTP server host to send emails, you can set it later in .env.smtp_user: (default: "") The SMTP server user to send emails, you can set it later in .env.smtp_password: (default: "") The SMTP server password to send emails, you can set it later in .env.emails_from_email: (default:"info@example.com") The email account to send emails from, you can set it later in .env.postgres_password: (default:"changethis") The password for the PostgreSQL database, stored in .env, you can generate one with the method above.sentry_dsn: (default: "") The DSN for Sentry, if you are using it, you can set it later in .env.
Backend docs: backend/README.md.
Frontend docs: frontend/README.md.
Deployment docs: deployment.md.
General development docs: development.md.
This includes using Docker Compose, custom local domains, .env configurations, etc.
Check the file release-notes.md.
- Based on Couchbase: https://github.com/tiangolo/full-stack-fastapi-couchbase.
- ⬆ Bump tiangolo/issue-manager from 0.2.0 to 0.5.0. PR #591 by @dependabot[bot].
- ✨ Upgrade items router with new SQLModel models, simplified logic, and new FastAPI Annotated dependencies. PR #560 by @tiangolo.
- ✨ Adopt SQLModel, create models, start using it. PR #559 by @tiangolo.
- ⬆️ Upgrade Python version and dependencies. PR #558 by @tiangolo.
- 🔧 Add missing dotenv variables. PR #554 by @tiangolo.
- ✨ Add
Not Foundpage. PR #595 by @alejsdev. - ✨ Add new pages, components, panels, modals, and theme; refactor and improvements in existing components. PR #593 by @alejsdev.
- ✨ Support delete own account and other tweaks. PR #614 by @alejsdev.
- ✨ Restructure folders, allow editing of users/items, and implement other refactors and improvements. PR #603 by @alejsdev.
- ✨ Add Copier, migrate from Cookiecutter, in a way that supports using the project as is, forking or cloning it. PR #612 by @tiangolo.
- ➕ Replace black, isort, flake8, autoflake with ruff and upgrade mypy. PR #610 by @tiangolo.
- ♻ Refactor items and services endpoints to return count and data, and add CI tests. PR #599 by @estebanx64.
- ✨ Add support for updating items and upgrade SQLModel to 0.0.16 (which supports model object updates). PR #601 by @tiangolo.
- ✨ Add dark mode to new-frontend and conditional sidebar items. PR #600 by @alejsdev.
- ✨ Migrate to RouterProvider and other refactors . PR #598 by @alejsdev.
- ✨ Add delete_user; refactor delete_item. PR #594 by @alejsdev.
- ✨ Add state store to new frontend. PR #592 by @alejsdev.
- ✨ Add form validation to Admin, Items and Login. PR #616 by @alejsdev.
- ✨ Add Sidebar to new frontend. PR #587 by @alejsdev.
- ✨ Add Login to new frontend. PR #585 by @alejsdev.
- ✨ Include schemas in generated frontend client. PR #584 by @alejsdev.
- ✨ Regenerate frontend client with recent changes. PR #575 by @alejsdev.
- ♻️ Refactor API in
utils.py. PR #573 by @alejsdev. - ✨ Update code for login API. PR #571 by @tiangolo.
- ✨ Add client in frontend and client generation. PR #569 by @alejsdev.
- 🐳 Set up Docker config for new-frontend. PR #564 by @alejsdev.
- ✨ Set up new frontend with Vite, TypeScript and React. PR #563 by @alejsdev.
- 📌 Add NodeJS version management and instructions. PR #551 by @alejsdev.
- Add consistent errors for env vars not set. PR #200.
- Upgrade Traefik to version 2, keeping in sync with DockerSwarm.rocks. PR #199.
- Run tests with
TestClient. PR #160.
- 🐛 Add
onClosetoSidebarItems. PR #589 by @alejsdev. - 🐛 Fix positional argument bug in
init_db.py. PR #562 by @alejsdev. - 📌 Fix flower Docker image, pin version. PR #396 by @sanggusti.
- 🐛 Fix Celery worker command. PR #443 by @bechtold.
- 🐛 Fix Poetry installation in Dockerfile and upgrade Python version and packages to fix Docker build. PR #480 by @little7Li.
- 🔧 Update .env to allow local debug for the backend. PR #618 by @tiangolo.
- ♻️ Refactor and update CORS, remove trailing slash from new Pydantic v2. PR #617 by @tiangolo.
- 🎨 Format files with pre-commit and Ruff. PR #611 by @tiangolo.
- 🚚 Refactor and simplify backend file structure. PR #609 by @tiangolo.
- 🔥 Clean up old files no longer relevant. PR #608 by @tiangolo.
- ♻ Re-structure Docker Compose files, discard Docker Swarm specific logic. PR #607 by @tiangolo.
- ♻️ Refactor update endpoints and regenerate client for new-frontend. PR #602 by @alejsdev.
- ✨ Add Layout to App. PR #588 by @alejsdev.
- ♻️ Re-enable user update path operations for frontend client generation. PR #574 by @alejsdev.
- ♻️ Remove type ignores and add
response_model. PR #572 by @alejsdev. - ♻️ Refactor Users API and dependencies. PR #561 by @alejsdev.
- ♻️ Refactor frontend Docker build setup, use plain NodeJS, use custom Nginx config, fix build for old Vue. PR #555 by @tiangolo.
- ♻️ Refactor project generation, discard cookiecutter, use plain git/clone/fork. PR #553 by @tiangolo.
- Refactor backend:
- Simplify configs for tools and format to better support editor integration.
- Add mypy configurations and plugins.
- Add types to all the codebase.
- Update types for SQLAlchemy models with plugin.
- Update and refactor CRUD utils.
- Refactor DB sessions to use dependencies with
yield. - Refactor dependencies, security, CRUD, models, schemas, etc. To simplify code and improve autocompletion.
- Change from PyJWT to Python-JOSE as it supports additional use cases.
- Fix JWT tokens using user email/ID as the subject in
sub. - PR #158.
- Simplify
docker-compose.*.ymlfiles, refactor deployment to reduce config files. PR #153. - Simplify env var files, merge to a single
.envfile. PR #151.
- ⬆ Upgrade code to support pydantic V2. PR #615 by @estebanx64.
- 📝 Update internal README and referred files. PR #613 by @tiangolo.
- 📝 Update README with in construction notice. PR #552 by @tiangolo.
- Add docs about reporting test coverage in HTML. PR #161.
- Add docs about removing the frontend, for an API-only app. PR #156.
- 👷 Add dependabot. PR #547 by @tiangolo.
- 👷 Fix latest-changes GitHub Action token, strike 2. PR #546 by @tiangolo.
- 👷 Fix latest-changes GitHub Action token config. PR #545 by @tiangolo.
- 👷 Add latest-changes GitHub Action. PR #544 by @tiangolo.
- Update issue-manager. PR #211.
- Add GitHub Sponsors button. PR #201.
- Simplify scripts and development, update docs and configs. PR #155.
- Make the Traefik public network a fixed default of
traefik-publicas done in DockerSwarm.rocks, to simplify development and iteration of the project generator. PR #150. - Update to PostgreSQL 12. PR #148. by @RCheese.
- Use Poetry for package management. Initial PR #144 by @RCheese.
- Fix Windows line endings for shell scripts after project generation with Cookiecutter hooks. PR #149.
- Upgrade Vue CLI to version 4. PR #120 by @br3ndonland.
- Remove duplicate
logintag. PR #135 by @Nonameentered. - Fix showing email in dashboard when there's no user's full name. PR #129 by @rlonka.
- Format code with Black and Flake8. PR #121 by @br3ndonland.
- Simplify SQLAlchemy Base class. PR #117 by @airibarne.
- Update CRUD utils for users, handling password hashing. PR #106 by @mocsar.
- Use
.instead ofsourcefor interoperability. PR #98 by @gucharbon. - Use Pydantic's
BaseSettingsfor settings/configs and env vars. PR #87 by @StephenBrown2. - Remove
package-lock.jsonto let everyone lock their own versions (depending on OS, etc). - Simplify Traefik service labels PR #139.
- Add email validation. PR #40 by @kedod.
- Fix typo in README. PR #83 by @ashears.
- Fix typo in README. PR #80 by @abjoker.
- Fix function name
read_itemand response code. PR #74 by @jcaguirre89. - Fix typo in comment. PR #70 by @daniel-butler.
- Fix Flower Docker configuration. PR #37 by @dmontagu.
- Add new CRUD utils based on DB and Pydantic models. Initial PR #23 by @ebreton.
- Add normal user testing Pytest fixture. PR #20 by @ebreton.
-
Fix security on resetting a password. Receive token as body, not query. PR #34.
-
Fix security on resetting a password. Receive it as body, not query. PR #33 by @dmontagu.
-
Fix SQLAlchemy class lookup on initialization. PR #29 by @ebreton.
-
Fix SQLAlchemy operation errors on database restart. PR #32 by @ebreton.
-
Fix locations of scripts in generated README. PR #19 by @ebreton.
-
Forward arguments from script to
pytestinside container. PR #17 by @ebreton. -
Update development scripts.
-
Create DB Item objects from all Pydantic model's fields.
-
Update Jupyter Lab installation and util script/environment variable for local development.
-
PR #14:
- Update CRUD utils to use types better.
- Simplify Pydantic model names, from
UserInCreatetoUserCreate, etc. - Upgrade packages.
- Add new generic "Items" models, crud utils, endpoints, and tests. To facilitate re-using them to create new functionality. As they are simple and generic (not like Users), it's easier to copy-paste and adapt them to each use case.
- Update endpoints/path operations to simplify code and use new utilities, prefix and tags in
include_router. - Update testing utils.
- Update linting rules, relax vulture to reduce false positives.
- Update migrations to include new Items.
- Update project README.md with tips about how to start with backend.
-
Upgrade Python to 3.7 as Celery is now compatible too. PR #10 by @ebreton.
- Fix frontend hijacking /docs in development. Using latest https://github.com/tiangolo/node-frontend with custom Nginx configs in frontend. PR #6.
-
Fix documentation for path operation to get user by ID. PR #4 by @mpclarkson in FastAPI.
-
Set
/start-reload.shas a command override for development by default. -
Update generated README.
- Simplify and update backend
Dockerfiles. - Refactor and simplify backend code, improve naming, imports, modules and "namespaces".
- Improve and simplify Vuex integration with TypeScript accessors.
- Standardize frontend components layout, buttons order, etc.
- Add local development scripts (to develop this project generator itself).
- Add logs to startup modules to detect errors early.
- Improve FastAPI dependency utilities, to simplify and reduce code (to require a superuser).
- Fix path operation to update self-user, set parameters as body payload.
Several bug fixes since initial publication, including:
- Order of path operations for users.
- Frontend sending login data in the correct format.
- Add https://localhost variants to CORS.
The Full Stack FastAPI Template is licensed under the terms of the MIT license.




