Your data lives in PostGIS and you want to serve it as WMS and WFS with GeoServer. This guide covers the full chain: prepare the table, create the store, publish the layer, give it an SLD style, find the service URLs and check the result in QGIS.
The idea: one table, one layer, two services
PostGIS stores geometries in PostgreSQL tables. GeoServer connects to them through a store, then exposes each selected table as a layer. That layer is served both as WMS, which returns an image drawn with a style, and as WFS, which returns the features themselves with their attributes.
The benefit of PostGIS as a source: the layer reflects the database live. A row added or fixed in the table appears in the service on the next request, without republishing. That is why the GeoServer, PostGIS, WMS and WFS combination is so common in spatial data infrastructures.
When should you use which? WMS suits display: the client receives a light image regardless of the number of features, and the style is the same for everyone. WFS suits analysis and reuse: the client receives geometries and attributes, and can filter, compute, export, or even edit if editing is enabled. In practice, the same layer is often published in both services, and each user picks what fits.
Prepare the PostGIS table
A few precautions in the database prevent most publishing errors: After a bulk import, also run VACUUM ANALYZE on the table: PostgreSQL updates its statistics and then picks the right query plan, spatial index included.
- A primary key. Without one, GeoServer cannot identify features reliably, and WFS-T editing becomes impossible.
- A typed geometry column with a declared coordinate system (SRID), for example geometry(MultiPolygon, 4326) or your national projection.
- A spatial index on the geometry, essential beyond a few thousand features.
- Simple names: lowercase, no spaces or accents, for the table and its columns.
- A dedicated read-only user where possible, rather than the database owner.
CREATE INDEX parcelles_geom_idx ON parcelles USING GIST (geom);
SELECT Find_SRID('public', 'parcelles', 'geom');Create the PostGIS store in GeoServer
In the GeoServer console, a PostGIS store belongs to a workspace. The usual procedure:
- Create or choose the workspace that will group the layers.
- Add a new store of type PostGIS.
- Fill in host, port, database, schema, user and password.
- Save: GeoServer tests the connection and lists the available tables.
On GeoServer Cloud, the PostGIS database of a LizPack Map instance links to your workspace in one click, with no connection form. Dedicated PostgreSQL and PostGIS databases run PostgreSQL 17 and PostGIS 3.5 over an encrypted connection.
The store form also offers connection settings that are often overlooked: the maximum number of open connections to the database, validating connections before use (helpful when the database restarts at night) and exposing primary keys, needed if you want to filter or edit by identifier. If you publish several schemas, create one store per schema: permissions and layer names stay readable.
Publish the layer and give it an SLD style
From the store's table list, publish the table you need. On the layer page: Use this page to fill in the layer title, abstract and keywords: they appear in GetCapabilities, and they are what your partners read in their software before they even display the map. A technical name such as parcelles_v2 says nothing; a clear title and a dated abstract save many questions.
- Check the declared and native coordinate systems.
- Compute the bounding boxes from the data (native, then latitude and longitude).
- In the publishing tab, choose the default style.
- Save, then check the layer preview.
The style is an SLD (Styled Layer Descriptor) file. You can write it by hand, start from a console template, or export it from QGIS (layer properties, symbology, save style as SLD). A minimal polygon style:
<PolygonSymbolizer>
<Fill><CssParameter name="fill">#2F7A3E</CssParameter></Fill>
<Stroke><CssParameter name="stroke">#083D44</CssParameter></Stroke>
</PolygonSymbolizer>Coordinate systems: the most common traps
GeoServer distinguishes the native system, the one the table is stored in, from the declared system, the one it advertises to clients. In most cases both are the same and match the SRID of the geometry column. When PostGIS does not know the SRID (a column declared as plain geometry, or an SRID of 0), GeoServer cannot guess the projection: the layer shows up in the wrong place, or not at all. The right fix is in the database, by declaring the correct SRID, rather than forcing a projection in GeoServer.
- On-the-fly reprojection. A client can request the layer in a system other than the storage one, for example EPSG:3857 for a web map. GeoServer reprojects every request: convenient, but it costs computation on large layers. If all your web maps work in EPSG:3857, a table or view already projected speeds up rendering.
- Axis order in WFS. With EPSG:4326, WFS versions 1.1.0 and 2.0.0 return coordinates in latitude then longitude order, while version 1.0.0 and most web tools expect longitude then latitude. Points that appear swapped, in the middle of the ocean, almost always come from this. Request the service version that suits your client, or a projected system.
- List of advertised systems. By default, the WMS GetCapabilities document may list thousands of coordinate systems, which makes it heavy and slows some clients down. The WMS service settings let you restrict that list to the few systems your users actually need.
- Recomputed bounding boxes. After a reprojection or a large data load outside the former area, recompute the layer bounding boxes: otherwise clients zoom to an area that is too small or ignore part of the features.
One simple rule avoids most of these issues: store your data in the official projection of your country or territory, declare it properly in PostGIS, and let GeoServer reproject for web uses. You keep accurate distance and area measurements in the database, and web maps that match the usual basemaps.
WMS and WFS service URLs
GeoServer exposes each service at its own URL, usually scoped to a workspace. The GetCapabilities document lists the available layers; it is what clients read.
| Service | What it returns | Typical URL |
|---|---|---|
| WMS | A styled map image. | https://example.org/geoserver/myworkspace/wms?service=WMS&request=GetCapabilities |
| WFS | Features and their attributes (GML, GeoJSON). | https://example.org/geoserver/myworkspace/wfs?service=WFS&request=GetCapabilities |
| WMTS | Pre-rendered tiles. | https://example.org/geoserver/gwc/service/wmts?request=GetCapabilities |
On GeoServer Cloud, each service has its own URL scoped to your workspace. For WFS, consider limiting the number of features returned per request if the table is large. Also check which output formats you offer: GML is the WFS default, while GeoJSON is easier for web developers to consume. Finally, publish the GetCapabilities URL, not the GetMap or GetFeature URLs, in your documentation: clients build their own requests from it, and it keeps working when you add or rename layers.
Querying the services: filters and formats
Once the layer is published, clients do not just display it: they query it. Knowing what requests look like helps you document your services for developers and diagnose what a piece of software actually asks for. Here is a WFS 2.0 request that returns at most fifty parcels from one municipality, as GeoJSON:
https://example.org/geoserver/myworkspace/wfs?service=WFS&version=2.0.0
&request=GetFeature&typeNames=myworkspace:parcelles
&count=50&outputFormat=application/json
&CQL_FILTER=commune='Dakar'- typeNames names the layer, prefixed with its workspace; in WFS 1.x the parameter is called typeName.
- count limits the number of features in WFS 2.0; the WFS 1.x equivalent is maxFeatures. Combined with startIndex, it lets you page through a large table.
- outputFormat selects the output format: GML by default, application/json for GeoJSON, which is easier to use in JavaScript.
- CQL_FILTER is a GeoServer-specific parameter that accepts readable attribute or spatial filters. XML OGC filters remain the standard route, understood by every server.
- bbox restricts the response to an extent, which QGIS and web libraries do automatically as you pan the map.
- propertyName limits the attributes returned, useful when the table has dozens of columns.
On the WMS side, the GetFeatureInfo request returns the attributes of the feature clicked on the map: Lizmap, QGIS and OpenLayers use it to show an information popup. Its content depends on the published attributes: if a field must not be seen, remove it from the published view rather than relying on the client to hide it.
WMS and WFS performance best practices
A layer that works with a hundred features can collapse with a million. Most slowdowns come from the database or the query, not from GeoServer itself. A few settings make most of the difference, and you set them once, at publishing time.
- Publish a view rather than a raw table. A PostgreSQL view, or an SQL view declared in GeoServer, returns only the useful columns and rows: less data transferred, less risk of exposing an internal field.
- Cap the features returned by WFS. Set a limit in the service configuration, and ask clients to filter by extent or attribute instead of downloading everything.
- Simplify for small scales. At country scale there is no need for meter-accurate outlines: a simplified table, or scale rules in the SLD style, lighten rendering.
- Cache what rarely changes. Basemaps and reference layers benefit from being served as WMTS from the built-in tile cache. Remember to clear the cache when data changes.
- Index filtered columns. If users filter by municipality or date, an index on those columns matters as much as the spatial index.
- Control WFS-T editing. Online editing is powerful, but it needs a database account allowed to write, per-layer permissions in GeoServer and a reliable primary key.
Finally, keep one simple security rule: the store's PostgreSQL user should only have the rights the layer needs. A read-only store cannot change anything, even if a service is misconfigured. For other precautions, see the guide securing and updating GeoServer.
Test in QGIS
- In QGIS, open the Data Source Manager.
- WMS/WMTS tab: create a connection with the WMS URL, connect, add the layer. The SLD style should display.
- WFS tab: create a connection with the WFS URL, add the layer, then open its attribute table to check the fields.
- Edit a row in PostGIS and refresh the map: the change appears.
If the layer is empty, check the bounding boxes and the SRID; if it is slow, the spatial index. To host it all without a server, see online GeoServer hosting. To choose between GeoServer and QGIS Server, read our comparison. Another quick check is the layer preview in the GeoServer console, which shows whether the problem lies in GeoServer or in the client. If the preview works but QGIS shows nothing, look at the connection URL, the requested coordinate system and any authentication settings in QGIS.
Common errors and fixes
The same problems come back from one project to the next. The table below starts from the symptom you observe, in QGIS or in a browser, and works back to the most likely cause.
| Symptom | Likely cause | Fix |
|---|---|---|
| The table does not appear in the store's list. | Wrong schema in the store, or a user without read rights on the table. | Check the configured schema and grant read access (GRANT SELECT) to the store's user. |
| The layer is published but the preview is empty. | Bounding boxes not computed, or missing SRID. | Recompute bounding boxes from the data and declare the SRID in PostGIS. |
| Points show up in the wrong place, with swapped coordinates. | Latitude and longitude axis order in WFS 1.1 or 2.0 with EPSG:4326. | Use a projected system, or the WFS version the client expects. |
| WFS returns an error or a huge file. | Unbounded request on a large table. | Set a feature limit on the service and filter by extent or attribute. |
| WFS-T editing fails on save. | No primary key, key not exposed, or read-only user. | Add a primary key, expose it in the store, grant write access on that table only. |
| An SLD style exported from QGIS renders badly. | QGIS symbols or labels with no SLD equivalent. | Simplify the style in QGIS before export, then adjust the SLD in the console. |
| New data does not appear on the map. | Old tiles served from the cache. | Clear the layer's tile cache after updating the data. |
When the symptom is not in this table, look at the server's raw response first: open the request URL in a browser. GeoServer returns an explicit exception message (unknown layer, missing attribute, SQL error) that map clients often hide behind an empty map. GeoServer and PostgreSQL logs complete the diagnosis, especially for SQL errors produced by a badly written view.
Frequently asked questions
How do I publish a PostGIS table in GeoServer?
Create a PostGIS store in a workspace, enter the database connection, then publish the table as a layer and compute its bounding boxes.
What is the difference between WMS and WFS in GeoServer?
WMS returns a map image drawn with a style. WFS returns the features themselves, geometry and attributes, which the client can query or export.
Why is my PostGIS layer not showing in GeoServer?
The most common causes are bounding boxes that were not computed, a missing or wrong SRID, or a table without a primary key. Check them on the layer page.
Can I use a QGIS style in GeoServer?
Yes, by exporting it as SLD from the layer properties in QGIS. Some advanced QGIS renderings have no SLD equivalent and need adjusting.
Do database changes show up in WFS?
Yes. GeoServer reads PostGIS on every request: an added or edited row is visible without republishing the layer, except in tiles already cached.