docs/published/handbook/engineering/manual-dev-setup.md
This documentation is deprecated, and likely not up to date. Please use the Flox-based instant setup instead.
In this step we will start all the external services needed by PostHog to work.
First, append line 127.0.0.1 kafka clickhouse clickhouse-coordinator objectstorage and line ::1 kafka clickhouse clickhouse-coordinator objectstorage to /etc/hosts. Our ClickHouse and Kafka data services won't be able to talk to each other without these mapped hosts.
You can do this with:
echo '127.0.0.1 kafka clickhouse clickhouse-coordinator objectstorage' | sudo tee -a /etc/hosts
echo '::1 kafka clickhouse clickhouse-coordinator objectstorage' | sudo tee -a /etc/hosts
If you are using a newer (>=4.1) version of Podman instead of Docker, the host machine's
/etc/hostsis used as the base hosts file for containers by default, instead of container's/etc/hostslike in Docker. This can make hostname resolution fail in the ClickHouse container, and can be mended by settingbase_hosts_file="none"incontainers.conf.
Now, start the Docker Compose stack:
docker compose -f docker-compose.dev.yml up
Friendly tip 1: If you see
Error while fetching server API version: 500 Server Error for http+docker://localhost/version:, it's likely that Docker Engine isn't running.
Friendly tip 2: If you see "Exit Code 137" anywhere, it means that the container has run out of memory. In this case you need to allocate more RAM in OrbStack settings.
Friendly tip 3: On Linux, you might need
sudo– see Docker docs on managing Docker as a non-root user. Or look into Podman as an alternative that supports rootless containers.
Friendly tip 4: If you see
Error: (HTTP code 500) server error - Ports are not available: exposing port TCP 0.0.0.0:5432 -> 0.0.0.0:0: listen tcp 0.0.0.0:5432: bind: address already in use, you have Postgres already running somewhere. Trydocker compose -f docker-compose.dev.ymlfirst, alternatively runlsof -i :5432to see what process is using this port.
sudo service postgresql stop
Second, verify via docker ps and docker logs (or via the OrbStack dashboard) that all these services are up and running. They should display something like this in their logs:
# docker ps NAMES
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5a38d4e55447 temporalio/ui:2.10.3 "./start-ui-server.sh" 51 seconds ago Up 44 seconds 0.0.0.0:8081->8080/tcp posthog-temporal-ui-1
89b969801426 temporalio/admin-tools:1.20.0 "tail -f /dev/null" 51 seconds ago Up 44 seconds posthog-temporal-admin-tools-1
81fd1b6d7b1b clickhouse/clickhouse-server:23.6.1.1524 "/entrypoint.sh" 51 seconds ago Up 50 seconds 0.0.0.0:8123->8123/tcp, 0.0.0.0:9000->9000/tcp, 0.0.0.0:9009->9009/tcp, 0.0.0.0:9440->9440/tcp posthog-clickhouse-1
f876f8bff35f bitnami/kafka:2.8.1-debian-10-r99 "/opt/bitnami/script…" 51 seconds ago Up 50 seconds 0.0.0.0:9092->9092/tcp posthog-kafka-1
d22559261575 temporalio/auto-setup:1.20.0 "/etc/temporal/entry…" 51 seconds ago Up 45 seconds 6933-6935/tcp, 6939/tcp, 7234-7235/tcp, 7239/tcp, 0.0.0.0:7233->7233/tcp posthog-temporal-1
5313fc278a70 postgres:12-alpine "docker-entrypoint.s…" 51 seconds ago Up 50 seconds (healthy) 0.0.0.0:5432->5432/tcp posthog-db-1
c04358d8309f zookeeper:3.7.0 "/docker-entrypoint.…" 51 seconds ago Up 50 seconds 2181/tcp, 2888/tcp, 3888/tcp, 8080/tcp posthog-zookeeper-1
09add699866e maildev/maildev:2.0.5 "bin/maildev" 51 seconds ago Up 50 seconds (healthy) 0.0.0.0:1025->1025/tcp, 0.0.0.0:1080->1080/tcp posthog-maildev-1
61a44c094753 elasticsearch:7.16.2 "/bin/tini -- /usr/l…" 51 seconds ago Up 50 seconds 9200/tcp, 9300/tcp posthog-elasticsearch-1
a478cadf6911 minio/minio:RELEASE.2022-06-25T15-50-16Z "sh -c 'mkdir -p /da…" 51 seconds ago Up 50 seconds 9000/tcp, 0.0.0.0:19000-19001->19000-19001/tcp posthog-object_storage-1
91f838afe40e redis:6.2.7-alpine "docker-entrypoint.s…" 51 seconds ago Up 50 seconds 0.0.0.0:6379->6379/tcp posthog-redis-1
# docker logs posthog-db-1 -n 1
2021-12-06 13:47:08.325 UTC [1] LOG: database system is ready to accept connections
# docker logs posthog-redis-1 -n 1
1:M 06 Dec 2021 13:47:08.435 * Ready to accept connections
# docker logs posthog-clickhouse-1 -n 1
Saved preprocessed configuration to '/var/lib/clickhouse/preprocessed_configs/users.xml'.
# ClickHouse writes logs to `/var/log/clickhouse-server/clickhouse-server.log` and error logs to `/var/log/clickhouse-server/clickhouse-server.err.log` instead of stdout/stsderr. It can be useful to `cat` these files if there are any issues:
# docker exec posthog-clickhouse-1 cat /var/log/clickhouse-server/clickhouse-server.log
# docker exec posthog-clickhouse-1 cat /var/log/clickhouse-server/clickhouse-server.err.log
# docker logs posthog-kafka-1
[2021-12-06 13:47:23,814] INFO [KafkaServer id=1001] started (kafka.server.KafkaServer)
# docker logs posthog-zookeeper-1
# Because ClickHouse and Kafka connect to Zookeeper, there will be a lot of noise here. That's good.
Friendly tip 1: Kafka is currently the only x86 container used, and might segfault randomly when running on ARM. Restart it when that happens.
Friendly tip 2: Checking the last Clickhouse log could show a
get_mempolicy: Operation not permittedmessage. However, it shouldn't affect the app startup - checking the whole log should clarify that Clickhouse started properly. To double-check you can get into the container and run a basic query.bash# docker logs posthog-clickhouse-1 # docker exec -it posthog-clickhouse-1 bash # clickhouse-client --query "SELECT 1"
Finally, install Postgres locally. Even if you are planning to run Postgres inside Docker, we need a local copy of Postgres (version 11+) for its CLI tools and development libraries/headers. These are required by pip to install psycopg2.
On macOS:
brew install postgresql
This installs both the Postgres server and its tools. DO NOT start the server after running this.
On Debian-based Linux:
sudo apt install -y postgresql-client postgresql-contrib libpq-dev
This intentionally only installs the Postgres client and drivers, and not the server. If you wish to install the server, or have it installed already, you will want to stop it, because the TCP port it uses conflicts with the one used by the Postgres Docker container.
On Linux, it's recommended to disable Postgres service by default, to ensure no port conflict arises. If postgres is already running on the port 5432, you can confirm it by checking the port, and then kill it manually.
sudo systemctl disable postgresql.service
sudo lsof -i :5432
sudo kill -9 `sudo lsof -t -i :5432`
On Linux you often have separate packages: postgres for the tools, postgres-server for the server, and libpostgres-dev for the psycopg2 dependencies. Consult your distro's list for an up-to-date list of packages.
brew install nvm or by following the instructions at https://github.com/nvm-sh/nvm. If using fish, you may instead prefer https://github.com/jorgebucaran/nvm.fish.Install the latest Node.js 22 (the version used by PostHog in production) with nvm install 22. You can start using it in the current shell with nvm use 22.
Install pnpm by running corepack enable and then running corepack prepare pnpm@10 --activate. Validate the installation with pnpm --version.
Install Node packages by running pnpm i.
Run pnpm --filter=@posthog/frontend typegen:write to generate types for Kea state management logics used all over the frontend.
The first time you run typegen, it may get stuck in a loop. If so, cancel the process (
Ctrl+C), discard all changes in the working directory (git reset --hard), and runpnpm typegen:writeagain. You may need to discard all changes once more when the second round of type generation completes.
brotli compression library and rust stable via rustup:On macOS:
brew install brotli rustup
rustup default stable
rustup-init
# Select 1 to proceed with default installation
On Debian-based Linux:
sudo apt install -y brotli
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# Select 1 to proceed with default installation
pnpm --filter=@posthog/nodejs install to install all required packages. We'll actually run the nodejs services in a later step.Note: If you face an error like
ld: symbol(s) not found for architecture arm64, most probably your openssl build flags are coming from the wrong place. To fix this, run:
export CPPFLAGS=-I/opt/homebrew/opt/openssl/include
export LDFLAGS=-L/opt/homebrew/opt/openssl/lib
pnpm --filter=@posthog/nodejs install
Note: If you face an error like
import gyp # noqa: E402, most probably need to installpython-setuptools. To fix this, run:
brew install python-setuptools
Troubleshooting nodejs services issues: If you encounter problems starting up the nodejs services, try these debugging steps:
cd nodejs
pnpm rebuild
pnpm i
Install a few dependencies for SAML to work. If you're on macOS, run the command below, otherwise check the official xmlsec repo for more details.
On macOS:
brew install libxml2 libxmlsec1 pkg-config
If installing
xmlsecdoesn't work, try updating macOS to the latest version (Sonoma).
On Debian-based Linux:
sudo apt install -y libxml2 libxmlsec1-dev libffi-dev pkg-config
Install Python 3.12.
On macOS, you can do so with Homebrew: brew install [email protected].
On Debian-based Linux:
sudo add-apt-repository ppa:deadsnakes/ppa -y
sudo apt update
sudo apt install python3.12 python3.12-venv python3.12-dev -y
Make sure when outside the venv to always use python3 instead of python, as the latter may point to Python 2.x on some systems. If installing multiple versions of Python 3, such as by using the deadsnakes PPA, use python3.12 instead of python3.
You can also use pyenv if you wish to manage multiple versions of Python 3 on the same machine.
uvuv is a very fast tool you can use for python virtual env and dependency management. See https://docs.astral.sh/uv/. Once installed you can prefix any pip command with uv to get the speed boost.
Create the virtual environment with the right Python version, and install dependencies - all in one with this command:
uv sync
Friendly tip: Creating an env could raise a
Failed to parsewarning related topyproject.toml. However, you should still see theActivate with:line at the very end, which means that your env was created successfully.
Activate the virtual environment:
# For bash/zsh/etc.
source .venv/bin/activate
# For fish
source .venv/bin/activate.fish
Install requirements with uv
If your workstation is an Apple Silicon Mac, the first time you install Python packages, you must set custom OpenSSL headers:
brew install openssl
CFLAGS="-I /opt/homebrew/opt/openssl/include $(python3.12-config --includes)" LDFLAGS="-L /opt/homebrew/opt/openssl/lib" GRPC_PYTHON_BUILD_SYSTEM_OPENSSL=1 GRPC_PYTHON_BUILD_SYSTEM_ZLIB=1 uv sync
Friendly tip: If you see
ERROR: Could not build wheels for xmlsec, refer to this issue.
These will be used when installing grpcio and psycopg2. After doing this once, and assuming nothing changed with these two packages, next time simply run:
uv sync
We now have the backend ready, and Postgres and ClickHouse running – these databases are blank slates at the moment however, so we need to run migrations to e.g. create all the tables:
cargo install sqlx-cli # If you haven't already
DEBUG=1 ./bin/migrate
Friendly tip 1: The error
fe_sendauth: no password suppliedconnecting to Postgres happens when the database is set up with a password and the user:pass isn't specified inDATABASE_URL. Tryexport DATABASE_URL=postgres://posthog:posthog@localhost:5432/posthog.
Friendly tip 2: You may run into
psycopg2errors while migrating on an ARM machine. Try out the steps in this comment to resolve this.
Friendly tip 3: When migrating, make sure the containers are running (detached or in a separate terminal tab).
Now start all of PostHog (backend, worker, nodejs services, and frontend – simultaneously):
hogli start
To customize which services to run, use hogli dev:setup to configure your dev environment interactively. This creates a profile that hogli start will use automatically.
Note: This command uses mprocs to run all development processes in a single terminal window. It will be installed automatically for macOS, while for Linux you can install it manually (
cargoornpm) using the official repo guide.
Friendly tip: If you get the error
Configuration property "enable.ssl.certificate.verification" not supported in this build: OpenSSL not available at build time, make sure your environment is using the rightopensslversion by setting those environment variables, and then runhogli startagain.
Open http://localhost:8010 to see the app.
Note: The first time you run this command you might get an error that says "layout.html is not defined". Make sure you wait until the frontend is finished compiling and try again.
To get some practical test data into your brand-new instance of PostHog, run DEBUG=1 ./manage.py generate_demo_data. For a list of useful arguments of the command, run DEBUG=1 ./manage.py generate_demo_data --help.
Friendly Tip The first time you run the app, you can log in with a test account: user:
[email protected]pwd:12345678.
This is it – you should be seeing the PostHog app at <a href="http://localhost:8010" target="_blank">http://localhost:8010</a>.
You can now change PostHog in any way you want. See Project structure for an intro to the repository's contents. To commit changes, create a new branch based on master for your intended change, and develop away.