The short answer
For a consultancy of roughly two to ten people, a strong, low-cost GIS stack is QGIS LTR for interactive analysis and cartography, GDAL/OGR for scripted conversion and reprojection, PostGIS on PostgreSQL (or GeoPackage for solo project files) for storage, and Git for the scripts and styles that make the whole thing reproducible. You add ArcGIS Pro seats selectively, where a client contract, a publishing platform, or a specialist toolbox genuinely requires it rather than as a default.
The constraint that shapes everything for a small team is that you have no dedicated GIS administrator. Whatever you choose has to be installable, repeatable, and handover-friendly by the same people who do the billable work. That pushes you toward tools that are scriptable and file/database formats that behave predictably across machines.
The four layers of the stack
It helps to separate the stack into four concerns and choose deliberately for each, rather than picking "a GIS" and hoping it covers everything.
1. Analysis and cartography (the desktop)
QGIS (use the Long Term Release, currently the 3.40 LTR line) is the workhorse. It handles vector editing, raster analysis, the full Processing toolbox, print layouts, and atlas-driven map series. Crucially for a consultancy, it reads and writes the formats clients send you: file geodatabases (read), shapefiles, GeoTIFF, LAS/LAZ point clouds via the PDAL provider, and almost anything else through the GDAL drivers underneath.
Keep ArcGIS Pro as a targeted second tool, not the foundation. The honest reasons to buy seats are: a client explicitly requires .aprx/.lyrx deliverables, you publish to ArcGIS Online or Enterprise, or you need a specific tool with no robust open-source equivalent (some geostatistics, utility/geometric networks, certain hydrology toolsets). A single floating or named-user licence shared across a small team is often enough because Pro is used for the last-mile deliverable, not daily analysis.
2. Batch processing and conversion (GDAL/OGR)
GDAL is the layer that turns ad-hoc clicking into a repeatable pipeline. Almost every "I'll just do it in the GUI" task that recurs should become a one-line command you can re-run and commit.
Reproject a raster to a project CRS (here ETRS89 / UTM zone 31N, EPSG:25831) with a clean resampling choice:
gdalwarp -t_srs EPSG:25831 -tr 10 10 -r bilinear \
-co COMPRESS=DEFLATE -co TILED=YES \
input_dem.tif dem_25831.tif
Convert and reproject a vector batch, writing into a GeoPackage instead of scattering shapefiles:
ogr2ogr -f GPKG -t_srs EPSG:25831 \
project.gpkg incoming/parcels.shp -nln parcels
Build overviews so large rasters draw fast in any client's QGIS:
gdaladdo -r average dem_25831.tif 2 4 8 16
These commands run identically on every analyst's laptop, which is exactly what a team without an admin needs.
3. Storage (GeoPackage and PostGIS)
Use GeoPackage (.gpkg) as the default project container for single-analyst work. It is a single SQLite file holding multiple vector and raster layers plus styles, it has no field-name truncation, and it avoids the shapefile sidecar mess. Treat the shapefile as an exchange format you receive and emit, never as your working store: the 10-character column-name limit and the ~2 GB per-file ceiling cause silent data loss.
Move to PostGIS the moment a dataset is edited by more than one person, exceeds comfortable file sizes, or needs server-side spatial queries. A small managed Postgres instance (a cheap cloud VM or a managed database) is enough. The payoff is concurrent editing, real indexing, and SQL such as:
SELECT a.id
FROM sites a
JOIN flood_zones f
ON ST_Intersects(a.geom, f.geom)
WHERE f.return_period = 100;
Always set and transform CRS explicitly in the database with ST_SetSRID and ST_Transform so geometry SRIDs never drift.
4. Reproducibility (Git and a data manifest)
The thing that separates a consultancy that can survive an analyst leaving from one that cannot is whether deliverables can be rebuilt. Put your GDAL/PyQGIS scripts, QGIS style files (.qml/.qlr), and SQL into a Git repository per project. Keep large raw inputs out of Git (they belong on shared storage or object storage) but record them in a short manifest: source, download date, CRS, and licence. Mark raw inputs read-only so processing always produces a fresh derived layer rather than mutating the source.
A worked example: standing up a new project
Suppose a three-person team wins an environmental screening job covering a 40 km corridor.
- Initialise.
git init, createraw/,derived/,scripts/, anddelivery/folders, and aMANIFEST.md. - Ingest. Drop client shapefiles and a national DEM tile into
raw/(read-only). Record each source in the manifest with its native CRS. - Normalise. Run one
scripts/01_reproject.shthatgdalwarps the DEM andogr2ogrs the vectors intoderived/project.gpkgat EPSG:25831. Commit the script. - Analyse. Do interactive work in QGIS against the GeoPackage; promote any repeated step (buffering, slope, intersection) into a Processing model or a PyQGIS script in
scripts/. - Collaborate. If two people need to edit the constraints layer simultaneously, push that one layer to PostGIS; keep the rest in the GeoPackage.
- Deliver. Export the print layout to
delivery/, and if the client demands Esri formats, open the GeoPackage in ArcGIS Pro on the one seat you keep and export the file geodatabase there.
The whole project rebuilds from raw/ plus scripts/, which is the test that matters when the work is audited a year later.
Common pitfalls and why they happen
- Choosing the stack by licence cost alone. Teams either over-buy Pro seats nobody uses or refuse to buy the one seat a contract requires, then waste days hand-converting formats. The cost that dominates is analyst time, not licence price.
- Treating shapefiles as the working format. Long attribute names get truncated to 10 characters and joins silently break. This happens because the shapefile spec predates modern needs; switch to GeoPackage early.
- Skipping the database until it is an emergency. Concurrent shapefile editing over a shared drive corrupts data. The symptom (locked files, lost edits) appears only once two people work the same layer, which is precisely when a deadline is near.
- Unscripted one-off processing. Anything done only in the GUI cannot be reproduced or reviewed. The cost is invisible until a client questions a number and nobody can show how it was derived.
Validating the stack
Before relying on the stack for a deliverable, confirm a few things: every working layer has an explicit, correct CRS (check with gdalsrsinfo or layer properties); a clean clone of the Git repo plus the raw inputs reproduces the derived layers byte-for-byte where possible; and at least one analyst other than the author can run the scripts on their own machine without manual fixes.
Bathyl perspective
We judge a stack by whether a deliverable can be rebuilt from source by someone who was not in the room when it was made. For small teams that means leaning on scriptable, open tools (QGIS, GDAL, PostGIS) for the day-to-day and reserving proprietary seats for genuine contractual or platform needs. The stack is infrastructure, so it should be boring, version-controlled, and explainable.
Related reading
- GIS Software Stack for Mining Projects
- GIS Software Stack for Environmental Projects
- Shapefile vs GeoPackage vs GeoJSON
- GIS and spatial analysis