- Add info about your project here
This template includes a dark/light mode toggle in the navbar. The preference is stored in localStorage and applied early to avoid a flash of incorrect theme.
This project keeps Django apps inside the /apps directory. This is both for human clarity and to help AI/code assistants put code in the right place.
apps/core: main app functionality (shared domain logic, base models, services, etc.)apps/docs: user-facing documentationapps/api: all API needs (Django Ninja routers, schemas, API-specific logic)apps/pages: landing/marketing pages (pricing, TOS, privacy policy, etc.)apps/blog: user-facing blog
When blog generation is enabled, the template includes a ready-to-extend blog post management API in apps/api/views.py:
POST /api/blog-posts/submitGET /api/internal/blog-postsGET /api/internal/blog-posts/{blog_post_id}PUT /api/internal/blog-posts/{blog_post_id}PATCH /api/internal/blog-posts/{blog_post_id}DELETE /api/internal/blog-posts/{blog_post_id}POST /api/internal/blog-posts/{blog_post_id}/reviewPOST /api/internal/blog-posts/{blog_post_id}/publish
These endpoints use superuser API auth by default and are intended as a starter baseline you can adapt for your product-specific content workflows.
Note: This should work out of the box with Render's free tier if you provide the AI API keys. Here's what you need to know about the limitations:
-
Worker Service Limitation: The worker service is not a dedicated worker type (those are only available on paid plans). For the free tier, I had to use a web service through a small hack, but it works fine for most use cases.
-
Memory Constraints: The free web service has a 512 MB RAM limit, which can cause issues with automated background tasks only. When you add a project, it runs a suite of background tasks to analyze your website, generate articles, keywords, and other content. These automated processes can hit memory limits and potentially cause failures.
-
Manual Tasks Work Fine: However, if you perform tasks manually (like generating a single article), these typically use the web service instead of the worker and should work reliably since it's one request at a time.
-
Upgrade Recommendation: If you do upgrade to a paid plan, use the actual worker service instead of the web service workaround for better automated task reliability.
Reality Check: The website functionality should be usable on the free tier - you'll only pay for API costs. Manual operations work fine, but automated background tasks (especially when adding multiple projects) may occasionally fail due to memory constraints. It's not super comfortable for heavy automated use, but perfectly functional for manual content generation.
If you know of any other services like Render that allow deployment via a button and provide free Redis, Postgres, and web services, please let me know in the Issues section. I can try to create deployments for those. Bear in mind that free services are usually not large enough to run this application reliably.
This should also be pretty streamlined. On your server you can create a folder in which you will have 2 files:
.env
Copy the contents of .env.example into .env and update all the necessary values.
docker-compose-prod.yml
Copy the contents of docker-compose-prod.yml into docker-compose-prod.yml and run the suggested command from the top of the docker-compose-prod.yml file.
How you are going to expose the backend container is up to you. I usually do it via Nginx Reverse Proxy with http://filebridge-backend-1:80 UPSTREAM_HTTP_ADDRESS.
Not recommended due to not being too safe for production and not being tested by me.
If you are not into Docker or Render and just wanto to run this via regular commands you will need to have 5 processes running:
python manage.py collectstatic --noinput && python manage.py migrate && gunicorn ${PROJECT_NAME}.asgi:application --bind 0.0.0.0:80 --workers 3 --worker-class uvicorn_worker.UvicornWorkerpython manage.py qclusternpm install && npm run startpostgresredis
You'd still need to make sure .env has correct values.
- Create 4 apps on CapRover.
filebridgefilebridge-workersfilebridge-postgresfilebridge-redis
-
Create a new CapRover app token for:
filebridgefilebridge-workers
-
Add Environment Variables to those same apps from
.env. -
Create a new GitHub Actions secret with the following:
CAPROVER_SERVERCAPROVER_APP_TOKENWORKERS_APP_TOKENREGISTRY_TOKEN
-
Then just push main branch.
-
Github Workflow in this repo should take care of the rest.
- Update the name of the
.env.exampleto.envand update relevant variables. - Run
uv sync - Run
uv run python manage.py makemigrations- Important: run this without specifying app names so Django detects changes across all apps.
- Do this before feature work and before first local run.
- Run
make serve- The frontend dev container now uses Node 24 and the backend waits for
frontend/build/manifest.jsonbefore booting, so the first page load should not race the asset pipeline.
- The frontend dev container now uses Node 24 and the backend waits for
- Run
make restart-workerjust in case, it sometimes has troubles connecting to REDIS on first deployment.
If you generated the project with use_ci = y, it includes a GitHub Actions workflow at .github/workflows/ci.yml that runs on pull requests.
It boots Postgres + Redis, runs python manage.py makemigrations --check --dry-run, then python manage.py check, and then runs pytest.
If you don’t want CI, set use_ci = n during Cookiecutter generation and the workflow will be removed.
This app uses Stripe Checkout for purchases and the Billing Portal for subscription management.
- Set the following in
.env:STRIPE_SECRET_KEYSTRIPE_PUBLISHABLE_KEY(optional, only needed for client-side Stripe.js)STRIPE_WEBHOOK_SECRETSTRIPE_PRICE_ID_MONTHLYSTRIPE_PRICE_ID_YEARLYWEBHOOK_UUID(optional, used to gate webhook URLs)
- Enable the Billing Portal in the Stripe Dashboard and allow subscription updates and cancellations.
- Create a webhook endpoint in the Stripe Dashboard:
- URL:
https://<your-domain>/stripe/webhook/<WEBHOOK_UUID>/(or/stripe/webhook/if no UUID) - Events:
customer.subscription.created,customer.subscription.updated,customer.subscription.deleted,checkout.session.completed
- URL:
- Use the Stripe CLI container (see
docker-compose-local.yml) to forward webhooks:docker compose -f docker-compose-local.yml run --rm stripe listen --forward-to http://backend:8000/stripe/webhook/${WEBHOOK_UUID}/
- Trigger a test event:
docker compose -f docker-compose-local.yml run --rm stripe trigger customer.subscription.created