phpIPAM Quickstart with Docker
phpIPAM running locally in a few minutes, with the discovery features that are its main advantage actually working.
Assumed: Docker and Docker Compose installed, and a machine that can reach the networks you want to scan. If you are choosing between tools first, see the tools reference.
Create the compose file
Create a directory and a docker-compose.yml:
services:
phpipam-web:
image: phpipam/phpipam-www:latest
ports:
- "8080:80"
environment:
- TZ=UTC
- IPAM_DATABASE_HOST=phpipam-mariadb
- IPAM_DATABASE_PASS=change-me
- IPAM_DATABASE_WEBHOST=%
restart: unless-stopped
volumes:
- phpipam-logo:/phpipam/css/images/logo
depends_on:
- phpipam-mariadb
phpipam-cron:
image: phpipam/phpipam-cron:latest
environment:
- TZ=UTC
- IPAM_DATABASE_HOST=phpipam-mariadb
- IPAM_DATABASE_PASS=change-me
- SCAN_INTERVAL=15m
restart: unless-stopped
depends_on:
- phpipam-mariadb
phpipam-mariadb:
image: mariadb:latest
environment:
- MARIADB_ROOT_PASSWORD=change-me
restart: unless-stopped
volumes:
- phpipam-db-data:/var/lib/mysql
volumes:
phpipam-db-data:
phpipam-logo:Replace both instances of change-me with the same password. They have to match, and mismatched values produce a database connection error at install time that does not say which side is wrong.
Pin the image tags to specific versions for anything beyond a trial. latest will upgrade the database schema underneath you at an unpredictable moment.
Start the stack and run the installer
docker compose up -d
docker compose psAll three containers should show as running. Open http://localhost:8080 and choose Automatic database installation. The credentials come from the environment variables, so the form should complete without further input.
If the installer reports it cannot connect, check the two password variables match and that the database container finished starting — MariaDB takes longer to initialise on first run than the web container takes to try connecting.
Change the default credentials
Sign in with admin / ipamadmin, and change the password immediately.
This matters more than it sounds. Default credentials on a network inventory tool are a well-known target, and an IPAM database is a map of your internal estate. Change it before you put anything real in.
While you are in the administration area, review Settings and disable anything you do not need. The default install enables several modules that widen the attack surface for no benefit.
Create a section and add subnets
Sections are organisational containers — one per site, environment, or customer works well. Go to Administration → Sections and create one.
Then add your top-level aggregates under Subnets. Enter the aggregate first, for example 10.0.0.0/8, then add children beneath it. phpIPAM nests subnets automatically by prefix, so a /16 added after its containing /8 appears in the right place without being told.
Structure the aggregates according to your address plan rather than inventing a structure here. If the plan does not exist yet, design it first — the tool will happily record a bad hierarchy.
Mark aggregates as Used for VLANs / Is folder where they exist only to hold children. Folders are containers rather than assignable ranges, which keeps utilization figures honest.
Import existing addresses from CSV
Open a subnet and use Import addresses rather than entering assignments by hand.
Prepare a CSV with a header row and columns matching phpIPAM’s fields — ip_addr, description, hostname, owner, state. The importer shows a mapping screen where columns are matched to fields, and a preview before anything is written.
Two things to check in the preview. Addresses outside the subnet’s range are rejected rather than reassigned, so a mismatch means you are importing into the wrong subnet. Duplicate addresses in the source file are a sign the spreadsheet you are migrating from already had conflicts — resolve them now, not after import.
Enable scanning and discovery
This is why you chose phpIPAM, and it does not happen by default.
Edit each subnet and enable:
- Check hosts status — pings recorded addresses and shows which are live
- Discover new hosts — finds addresses in use that are not recorded
The phpipam-cron container runs these on the SCAN_INTERVAL set in the compose file.
The frequent failure here is reachability. The cron container is on a Docker bridge network and has no route to your internal subnets. For scanning to work it needs one — commonly network_mode: host on the cron service for a local install, or deployment onto a host with the right connectivity. Verify before assuming the scanner is broken:
docker compose exec phpipam-cron ping -c 2 10.1.1.1Once scanning works, the Discover new hosts results are your reconciliation data: addresses in use that nobody recorded. Each one is a future conflict. Working through that list is the highest-value thing you will do in the first week.
Before running this in production
The compose file above is a starting point, not a deployment.
- Put TLS in front of it. The web container serves plain HTTP. Run it behind a reverse proxy with a certificate.
- Back up the database.
docker compose exec phpipam-mariadb mysqldumpon a schedule, restored somewhere else at least once to prove it works. - Pin image versions. As above.
- Set up authentication properly. phpIPAM supports LDAP, AD, and SAML. Local accounts do not scale and do not get removed when people leave.
- Create an API key under Administration → API if anything will read from phpIPAM programmatically. Records that something depends on stay accurate; records nothing reads do not.
Related
- NetBox quickstart — the source-of-truth alternative
- Open-source IPAM — what self-hosting commits you to
- Address plan design — design the hierarchy before entering it
- IPAM best practices — keeping the data accurate afterwards