Pulse is a locally deployable Python application built around a FastAPI API service, a Celery worker, Redis, and two Postgres shards. The Docker Compose setup runs:
apivia Uvicornworkervia Celeryredispostgres_shard0postgres_shard1
That layout is defined in the current docker-compose.yml. The API and worker both build from the same Docker image and differ only by startup command. Postgres shard 0 listens on host port 5432, shard 1 on 5433, and Redis on 6379.
This README assumes the repo contains:
- a
Dockerfile docker-compose.yml.env.docker.env.kubernetes- Kubernetes manifests under
k8s/
It also assumes the Kubernetes setup now uses:
- service names like
pulse-postgres-shard0,pulse-postgres-shard1, andpulse-redis - local Postgres storage via
emptyDirfor local development - separate migrate and bootstrap jobs that are run after the databases are up
- Docker Engine
- Docker Compose plugin
- Docker
kubectlkind
Make sure .env.docker exists and contains the Docker-specific values for:
- Postgres credentials
- Redis settings
- any application secrets
- Docker hostnames expected by the app
docker compose up --builddocker compose up --build -ddocker compose psdocker compose logs -fTo follow a single service:
docker compose logs -f api
docker compose logs -f worker
docker compose logs -f postgres_shard0
docker compose logs -f postgres_shard1
docker compose logs -f redisThe API is published on port 8000:
http://localhost:8000
If Swagger is enabled:
http://localhost:8000/docs
docker compose downTo also remove volumes:
docker compose down -vThis section is for the local Kubernetes version of the project.
Make sure .env.kubernetes exists and uses Kubernetes service names, not Docker Compose names.
For example, use:
POSTGRES_SHARD0_HOST=pulse-postgres-shard0
POSTGRES_SHARD1_HOST=pulse-postgres-shard1
REDIS_HOST=pulse-rediskind create cluster --name pulseVerify:
kubectl get nodes
kubectl get pods -n kube-systemYou should see CoreDNS running before deploying the app.
From the repo root:
docker build -t pulse-app:latest .kind load docker-image pulse-app:latest --name pulsekubectl apply -f k8s/namespace.yamlkubectl delete secret pulse-secrets -n pulse --ignore-not-found
kubectl create secret generic pulse-secrets \
--namespace pulse \
--from-env-file=.env.kubernetesApply the core services first:
kubectl apply -f k8s/configmap.yaml
kubectl apply -f k8s/redis-deployment.yaml
kubectl apply -f k8s/redis-service.yaml
kubectl apply -f k8s/postgres-shard0-deployment.yaml
kubectl apply -f k8s/postgres-shard0-service.yaml
kubectl apply -f k8s/postgres-shard1-deployment.yaml
kubectl apply -f k8s/postgres-shard1-service.yaml
kubectl apply -f k8s/api-deployment.yaml
kubectl apply -f k8s/api-service.yaml
kubectl apply -f k8s/worker-deployment.yamlkubectl get pods -n pulse -wWait until these are running:
pulse-apipulse-workerpulse-redispulse-postgres-shard0pulse-postgres-shard1
For the local emptyDir Postgres setup, the pulse database may need to be created before migrations run.
Create it on both shards:
kubectl exec -it deployment/pulse-postgres-shard0 -n pulse -- psql -U postgres -c "CREATE DATABASE pulse;"
kubectl exec -it deployment/pulse-postgres-shard1 -n pulse -- psql -U postgres -c "CREATE DATABASE pulse;"If POSTGRES_DB=pulse is already handled by your manifests and first-time init path, this step may not be necessary.
kubectl delete job pulse-migrate -n pulse --ignore-not-found
kubectl apply -f k8s/migrate-job.yaml
kubectl logs job/pulse-migrate -n pulse -fkubectl delete job pulse-bootstrap -n pulse --ignore-not-found
kubectl apply -f k8s/bootstrap-job.yaml
kubectl logs job/pulse-bootstrap -n pulse -fPort-forward the service:
kubectl port-forward svc/pulse-api 8000:8000 -n pulseThen open:
http://localhost:8000
If Swagger is enabled:
http://localhost:8000/docs
kubectl get all -n pulse
kubectl get svc -n pulse
kubectl get endpoints -n pulse
kubectl logs deployment/pulse-api -n pulse
kubectl logs deployment/pulse-worker -n pulsedocker compose downDelete app resources:
kubectl delete namespace pulseDelete the local kind cluster:
kind delete cluster --name pulse- If the API or worker cannot connect to Postgres, verify
.env.docker - If a port is already in use, stop the conflicting service or adjust ports
- Use
docker compose logs -fto inspect failures
- If service names do not resolve, verify CoreDNS is running:
kubectl get pods -n kube-system- If migrations fail with host resolution errors, verify
.env.kubernetesuses Kubernetes service names - If Postgres comes up but migrations fail with
database "pulse" does not exist, create the database on both shards before running the migration job - If the bootstrap job fails, inspect the job command and logs:
kubectl logs job/pulse-bootstrap -n pulse- Docker Compose and Kubernetes use different service names. Keep
.env.dockerand.env.kubernetesseparate. - For local Kubernetes development,
emptyDiris acceptable for Postgres, but it is ephemeral. - Because Postgres data is ephemeral in the local Kubernetes setup, restarting the Postgres deployments may require recreating the
pulsedatabase and rerunning migrate/bootstrap.