Markdown can't be served

Hello, I’m trying to serve a panel app behind NGINX, but I unfortunately run into the following error when I put any Markdown panes into my panel app:

#!/usr/bin/env python
# coding: utf-8

# In[9]:

import socket

import pyfesom2 as pf
import as ccrs
import numpy as np
import geoviews as gv
from holoviews.operation.datashader import rasterize, datashade
import pandas as pd
import panel as pn
import holoviews as hv
import os


def plot_mesh(mesh_path):
    a, b, g = [0, 0, 0]
    if os.path.isfile(f"{mesh_path}/abg.dat"):
        with open(f"{mesh_path}/abg.dat") as abg:
            a, b, g = [int(x) for x in abg.readlines()[0].strip().split(",")] 
    mesh = pf.load_mesh(
        abg=[a, b, g],

    title = "Fesom Resolution (NUM_2D_NODES 2d Nodes)"
    if os.path.isfile(mesh_path + "/title.txt"):
        with open(mesh_path +"/title.txt") as tit_file:
            title =

    title = title.replace("NUM_2D_NODES", "{:,}")

    node_ids_and_area = np.concatenate(
                ((2 * mesh.voltri[mesh.no_cyclic_elem]) ** 0.5) / 1000.0, axis=-1

    df = pd.DataFrame(node_ids_and_area)  # = "Element ID"
    df.columns = ["Node 1", "Node 2", "Node 3", "Edge Length"]
    # df.head()

    nodes = gv.Points((mesh.x2, mesh.y2))

    trimesh = gv.TriMesh((df, nodes)).redim(
        x="Longitude (˚E)", y="Latitude (˚N)", z="Resolution (m**2)"

    projection = ccrs.PlateCarree()
    projected_trimesh = gv.project(trimesh, projection=projection)

    FESOM_Resolution = rasterize(projected_trimesh).opts(
        clabel="Resolution as Average Element Edge Length (km)",
        clim=(20, 180),
    return FESOM_Resolution

meshes = []
for user in os.listdir("/home/csys/"):
    if os.path.isdir(f"/home/csys/{user}/public/FESOM_Meshes/"):
        for root, dirs, files in os.walk(f"/home/csys/{user}/public/FESOM_Meshes/"):
            for dir_ in dirs:
                    print(f"Tying to load up {root}/{dir_}")
                    meshes.append((f"{root}/{dir_}", plot_mesh(f"{root}/{dir_}")))
                except Exception as e:
                    pass # Error with that mesh...

FESOM_Resolution = pn.Tabs(*meshes)
gs = pn.GridSpec(sizing_mode="stretch_both", max_height=800)
#gs[0, 0] = "# Fesom Mesh Gallery"
gs[1, 0] = FESOM_Resolution
gs[2, 0] = "You may add your own mesh here by placing the mesh folder in `${HOME}/public/FESOM_Meshes/`.\nA comma-seperated file called `abg.dat` in the mesh folder will set the alpha, beta, and gamma values for rotated/non-rotated meshes.\n.A file called `title.txt` will set the mesh title. To include the number of 2d nodes, use the string `NUM_2D_NODES`. It will be replaced automatically."
#gs[3, 0] = "<p style='color=gray'><em>The Fesom Mesh Gallery is maintained by Paul Gierz</em></p>"

Here’s the minimal NGINX config I’m using. I tried hooking up the static files as described in the Bokeh documentation, but that didn’t work either:

  server {
	location /meshes {
		proxy_pass http://localhost:6001;
		proxy_set_header Upgrade $http_upgrade;
		proxy_set_header Connection "upgrade";
		proxy_http_version 1.1;
		proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
		proxy_set_header Host $host:$server_port;
		proxy_buffering off;

	#location /static {
	#	alias  /opt/miniconda3/lib/python3.7/site-packages/bokeh/server/static;


Hi @pgierz

I have not tried nginx. But I can see your example is not identical to the bokeh example here

Could you try to test if that works first (maybe you have)?

Also I would make sure to add the log files and inspect them to see what goes on on the NGINX side.

Also in your example do you panel serve .... --port 6001?

Hi @Marc,

I did the following:

  1. added the logs
  2. Uncommented the /static location
  3. Restarted the server.

I now get some more info, specifically: - - [04/Aug/2020:10:18:25 +0200] "GET /static/extensions/panel/panel.min.js HTTP/1.1" 404 152 "" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.1 Safari/605.1.15" 

So, it looks like it cannot find the specific panel.min.js. Is there a way to configure NGINX to serve (or I guess, combine?) two different static locations? One for the standard bokeh, and one for the panel specific things?

1 Like

I’ve had this issue wit panel.min.js too.

Just do:

export BOKEH_RESOURCES="inline"

before serving

1 Like

@Material-Scientist thanks! That seems to have solved the problem. I’m not an expert for web servers, so I don’t know if that places any unneeded load on the system, but it works for now.

Another way I found (which broke once I cleared my browser cache):
In the NGINX server block:

  server {
	listen 80 default_server;
	server_name _;

	access_log  /tmp/bokeh.access.log;
	error_log   /tmp/bokeh.error.log debug;

	location /meshes {
		proxy_pass http://localhost:6001;
		proxy_set_header Upgrade $http_upgrade;
		proxy_set_header Connection "upgrade";
		proxy_http_version 1.1;
		proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
		proxy_set_header Host $host:$server_port;
		proxy_buffering off;

	location /static {
		alias  /opt/miniconda3/lib/python3.7/site-packages/bokeh/server/static;

	location /static/extensions/panel {
	alias /opt/miniconda3/lib/python3.7/site-packages/panel/dist; 



Thanks for sharing @Material-Scientist and @pgierz. I believe all of us are new to this. So if we could end up with some example that works and mark it a solution it would be GREAT. And if someone could contribute something to the Panel Docs it would be AWESOME.


1 Like