Start
Revisit your travel memories
Open source travel documentation app for self-hosting or serverless usage
Lightweight and future proof
Try out the online version using example data or your own data.
Background
The idea for this application came to me while reflecting on the countless journeys I had taken. Despite all the places I had visited, I found myself forgetting key moments and locations. Friends would ask about my travels, and I often struggled to recall the details or find the photos that matched those memories. I realized I needed a way to not only document my trips but also retrieve the content of the documentation with ease.
That is how Immer in Bewegung was born. The name, meaning always in movement in German, perfectly encapsulates the heart of this project. To bring this concept to life, the frog is chosen as the mascot—a symbol of endless motion and adventure.
Note
You can check out the online app running at https://online.bewegung.app/. If you want to bind your own data, you can do so via https://online.bewegung.app/?path=source.
Basic idea
Over time, most code in applications becomes obsolete. To ensure your memories are not lost to such obsolescence, the core idea of this project is to visualize travel data stored in SQLite format. SQLite databases are easy to update using any of the many available tools, offer long-term reliability, and are more likely to remain compatible with future technologies. This makes it a solid, future-proof solution. However, scrolling through a database directly can be tedious—so why not build an interface on top? But this project is not just an application; it’s a versatile syntax that allows you to take travel notes in a simple and structured way.
Three main features
- A single SQLite database compliant with the Immer in Bewegung Trip Syntax
- Use directly on GitHub or host it on your own server
- Use in conjunction with Immich to combine your documentation with photos (optional)
Made in Sweden
Quick Start
Usage Combinations
| Serverless usage | Self-hosting | |
|---|---|---|
| Configuration | Via settings in database file | Via settings in database file |
| Database | Your own database file that you bind via the browser File API | Your own database file that you bind via the browser File API or database file stored on a server |
Serverless usage (simplest choice)
Download the database template (read more about how to edit the file under Create the dataset
https://github.com/plans-coding/immer-in-bewegung/raw/refs/heads/main/personal/bewegung.db
To bind your own SQLite file to the online application, go to
https://online.bewegung.app/?path=source
Afterwards just start browsing your trips at
Host on your own server
If you want your app available from many devices without need to bind it specifillay on each device, you can develop your instance to your own server instead.
Step 1 − Download files
Go to the directory where you want to place your files, e.g.
cd /var/www/iib
Download the latest release and extract to the folder
Step 2 − Edit settings
Specify your settings and the path to the database file in server_db_path.txt. Read more at Settings.
Create the dataset
Database Structure
| Table | Type | Description |
|---|---|---|
| bewa_Overview | Table | The table that defines the extent of your trips. One trip per row. |
| bewb_Events | Table | The table containg detailed notes from your trips. One row per day (date). |
| bewx_Settings | Table | Where the settings for the app is stored. |
For more detailed instructions, follow the Trip Syntax.
Tip
Download a pre-filled SQLite template file and edit the tables within.
Note
To obtain coordinates for a given position, you can use the Coordinate Tool and easily copy and paste the coordinates into the SQLite file.
Trip Syntax
Table: Overview
The first table in your SQLite file is named bewa_Overview with fields according to following.
Note
Regarding validation: Some fields are checked for syntax and must comply to that.
| Column | Description |
|---|---|
| InnerId | The InnerId is used to connect each row to date notes in table bewb_Events. It consists of a six characters long string generated randomly in Sqlite with lower(hex(randomblob(3)))Syntax: <xxxxxx> |
| ParticipantGroup | Could e.g. be Family, Private, or Work. Syntax: <string> |
| OuterId | The OuterID is exposed to end-user of the web app. Normally you can take the first letter of the TripDomain and the first letter of ParticipantGroup and add a chronological number. Syntax: <letters and dashes and serial numbers of your choice> |
| OverallDestination | Type in you trip primary destination. E.g. Finland, or Italia, Spain etc. Syntax: <string> |
| DepartureDate | Your departure date in ISO format. Syntax: YYYY-MM-DD |
| ReturnDate | Your return date in ISO format. Syntax: YYYY-MM-DD |
| MapPins | The main stops of your trip that you want to plot on the map. Syntax (multi-line accepted): [ NAME_OF_MAP_PIN ]( LATITUDE, LONGITUDE ) |
| TripDescription | A short description that explain the aim of the trip. E.g. My fantastic summer trip to France. Syntax: <string> |
| PhotoStarttime | For use with Immich. If you leave your home at let say 8 pm and don’t want to include photos from earlier on departure day, then you can set a time here. If left empty, all photos from departure day will be included. Syntax: HH:MM |
| PhotoEndtime | For use with Immich. Same as above, but for return day. Syntax: HH:MM |
Table: Events
The second table in your SQLite file is named bewb_Events with fields according to following.
Note
Regarding validation: Some fields are checked for syntax and must comply to that.
| Column Name | Description |
|---|---|
| InnerId | Reference to corresponding InnerId in table bewa_Overview.Syntax: <xxxxxx> |
| Date | Your date in ISO format. Syntax: YYYY-MM-DD |
| Events | This is your fully description of what you did this day. Syntax (multi-line accepted): <string> |
| Accommodation | The name and the address of your accommodation. Syntax: <string> |
| AccommodationCountry | The name of the country you stayed in during the night. Syntax: <string> |
| AccommodationCoordinateAccuracy | Possibility to mark the accuracy of the coordinates. Syntax: <string> |
| AccommodationCoordinates | The gps coordinates of your accommodation, e.g. 59.329444, 18.068611. Syntax: lat_decimal,lng_decimal |
| TravelParticipants | The name of your travel buddies, separated by comma. N.B. Not exposured in web app. Syntax: <string> |
| AdditionalNotes | N.B. Additional notes not exposured in web app. Syntax (multi-line accepted): <string> |
| CountriesDuringDay | This field is for enhanced statistics. Enter all countries in cronologial order during that day, separated by comma. The country names need to conform to the country names defined in ContinentCountries. In front of the country name prefix *, **, and + is allowed. Read more under Special syntax used for Countries During Day (Events).Syntax (prefix allowed): <country name without space>, <country name without space>, <country name without space> [...] |
Special syntax used for Countries During Day (Events)
| Prefix | Function |
|---|---|
| * | Shorter visit of significant importance |
| ** | Very short visit of without significant importance |
| + | Restore, counts only if * and ** count |
Note
Countries During Day: When a prefix is used in front of a country name in field CountriesDuringDay in table
bewb_Eventsit affect the Statistics page.
Settings
Path to database
Change the relative path to the database file in server_db_path.txt if you want your own database to load automatically when self-hosting.
Database file
The settings is configured the database file table bewx_Settings. The following fields are applicable
| Attribute Group | Attribute | Value | Description |
|---|---|---|---|
| Base | HomeCountry | Country | Use this to remove your own country from the trip unique countries field. Use same spelling as in your notes and in the value of ContinentCountries. |
| Base | LanguageFile | Filename | Filename to the translation file located in the personal folder, e.g. swedish.json. |
| Definition | ContinentCountries | List | Definitions of continents and countries as well as their language (and spelling). |
| Definition | TripDomainColors | List | Color definitions of your trip domains. |
| Base | Immich | Disabled or Enabled | Enable if you want filter links from all event dates to Immich. |
| Photos | ImmichApiKey | Key | |
| Photos | ImmichCoverAlbumId | GUID | The GUID of the cover photo album in Immich. |
| Feature | ImmichUrl | URL | Online URL to the Immich installation (e.g. https://immich.example.com/). |
| Other | Dataset | Disabled or Enabled | If enabled, the link to Dataset is shown. |
| Other | ExternalMapProvider | URL | Set your prefered map provider. |
Trip Domains
There are three pre-defined trip domains
- Domestic trip
- Trip abroad
- Attachment trip
An attachment trip is defined as a visit to a country where you have a deeper connection. For example, if you study abroad, you might want to document that time but distinguish it from the ordinary abroad or domestic category.
Set theme colors to your different TripDomains.
Domestic:#0b5394
Abroad:#1d655e
Attatchment:#C60C30
Participant Groups
Add the names of your different travel groups.
Syntax: <string without space>
Continent Countries
If you want to change (make other country definitions or translate country names) the pre-defined country settings you can do it by changing in the country definitions. Only countries defined in the table ContinentCountries can be used in your trip notes and only with the very exact spelling.
Tip
Other language: If you have the countries in
bewb_Events(CountriesDuringDay) written in another languange than English, then you can change the app behaviour by translating the countries inContinentCountriesto your own language.
Europe:Albania
Europe:Andorra
Europe:Austria
Europe:Belarus
Europe:Belgium
Europe:Bosnia and Herzegovina
Europe:Bulgaria
Europe:Croatia
Europe:Cyprus
Europe:Czech Republic
Europe:Denmark
Europe:Estonia
Europe:Finland
Europe:Finland-Åland
Europe:France
Europe:Georgia
Europe:Germany
Europe:Greece
Europe:Hungary
Europe:Iceland
Europe:Ireland
Europe:Italy
Europe:Kosovo
Europe:Latvia
Europe:Liechtenstein
Europe:Lithuania
Europe:Luxembourg
Europe:Malta
Europe:Moldova
Europe:Moldova-Transnistria
Europe:Monaco
Europe:Montenegro
Europe:Netherlands
Europe:North Macedonia
Europe:Norway
Europe:Poland
Europe:Portugal
Europe:Romania
Europe:Russia
Europe:San Marino
Europe:Serbia
Europe:Slovakia
Europe:Slovenia
Europe:Spain
Europe:Sweden
Europe:Switzerland
Europe:Ukraine
Europe:United Kingdom
Europe:United Kingdom-Akrotiri and Dhekelia
Europe:United Kingdom-Gibraltar
Europe:United Kingdom-Jersey
Europe:Vatican City
Asia:Afghanistan
Asia:Armenia
Asia:Azerbaijan
Asia:Bahrain
Asia:Bangladesh
Asia:Bhutan
Asia:Brunei
Asia:Cambodia
Asia:China
Asia:Cyprus
Asia:Georgia
Asia:India
Asia:Indonesia
Asia:Iran
Asia:Iraq
Asia:Israel
Asia:Japan
Asia:Jordan
Asia:Kazakhstan
Asia:Kuwait
Asia:Kyrgyzstan
Asia:Laos
Asia:Lebanon
Asia:Malaysia
Asia:Maldives
Asia:Mongolia
Asia:Myanmar
Asia:Nepal
Asia:North Korea
Asia:Oman
Asia:Pakistan
Asia:Philippines
Asia:Qatar
Asia:Russia
Asia:Saudi Arabia
Asia:Singapore
Asia:South Korea
Asia:Sri Lanka
Asia:Syria
Asia:Taiwan
Asia:Tajikistan
Asia:Thailand
Asia:Timor-Leste
Asia:Turkey
Asia:Turkmenistan
Asia:United Arab Emirates
Asia:Uzbekistan
Asia:Vietnam
Asia:Yemen
North America:Antigua and Barbuda
North America:Bahamas
North America:Barbados
North America:Belize
North America:Canada
North America:Costa Rica
North America:Cuba
North America:Dominica
North America:Dominican Republic
North America:El Salvador
North America:Grenada
North America:Guatemala
North America:Haiti
North America:Honduras
North America:Jamaica
North America:Mexico
North America:Nicaragua
North America:Panama
North America:Saint Kitts and Nevis
North America:Saint Lucia
North America:Saint Vincent and the Grenadines
North America:Trinidad and Tobago
North America:United States
South America:Argentina
South America:Bolivia
South America:Brazil
South America:Chile
South America:Colombia
South America:Ecuador
South America:Guyana
South America:Paraguay
South America:Peru
South America:Suriname
South America:Uruguay
South America:Venezuela
Africa:Algeria
Africa:Angola
Africa:Benin
Africa:Botswana
Africa:Burkina Faso
Africa:Burundi
Africa:Cabo Verde
Africa:Cameroon
Africa:Central African Republic
Africa:Chad
Africa:Comoros
Africa:Democratic Republic of the Congo
Africa:Djibouti
Africa:Egypt
Africa:Equatorial Guinea
Africa:Eritrea
Africa:Eswatini
Africa:Ethiopia
Africa:Gabon
Africa:Gambia
Africa:Ghana
Africa:Guinea
Africa:Guinea-Bissau
Africa:Ivory Coast
Africa:Kenya
Africa:Lesotho
Africa:Liberia
Africa:Libya
Africa:Madagascar
Africa:Malawi
Africa:Mali
Africa:Mauritania
Africa:Mauritius
Africa:Morocco
Africa:Mozambique
Africa:Namibia
Africa:Niger
Africa:Nigeria
Africa:Republic of the Congo
Africa:Rwanda
Africa:Sao Tome and Principe
Africa:Senegal
Africa:Seychelles
Africa:Sierra Leone
Africa:Somalia
Africa:South Africa
Africa:South Sudan
Africa:Sudan
Africa:Tanzania
Africa:Togo
Africa:Tunisia
Africa:Uganda
Africa:Zambia
Africa:Zimbabwe
Oceania:Australia
Oceania:Fiji
Oceania:Kiribati
Oceania:Marshall Islands
Oceania:Micronesia
Oceania:Nauru
Oceania:New Zealand
Oceania:Palau
Oceania:Papua New Guinea
Oceania:Samoa
Oceania:Solomon Islands
Oceania:Tonga
Oceania:Tuvalu
Oceania:Vanuatu
Translations
App translation
If the app is not available in your language you can easily translate it by placing a file in the folder personal/ and change the value in settings table bewx_Settings.
Read more under Settings.
Continent and Country translations
The continent and countries can be translated, read more Continent Countries.
Trip Domain translation
The trip domains can be translated, read more Trip Domains.
Immich Integration
Hide Immich search bar
Tip
When Immer in Bewegung embeds photos in image view (see below), the search bar from Immich will be displayed unless you use small a css modification in your Immich settings.
Add
body:has(#search-chips:target) #asset-selection-app-bar,
body:has(#search-chips:target) #search-chips {
display:none;
}
body:has(#search-chips:target),
body:has(#search-chips:target) #search-content {
background-color: #000000;
}
to User Icon > Admininstration > Settings > Theme Settings: Custom CSS.

Usage of Immich API
To make use of Immich API you need to allow CORS, example snippet to add to /etc/caddy/Caddyfile
(cors) {
@cors_preflight{args[0]} method OPTIONS
@cors{args[0]} header Origin {args[0]}
handle @cors_preflight{args[0]} {
header {
Access-Control-Allow-Origin "{args[0]}"
Access-Control-Allow-Methods "GET, POST, PUT, PATCH, DELETE, OPTIONS"
Access-Control-Allow-Headers *
Access-Control-Max-Age "3600"
defer
}
respond "" 204
}
handle @cors{args[0]} {
header {
Access-Control-Allow-Origin "{args[0]}"
Access-Control-Expose-Headers *
defer
}
}
}
immich.your-server-name.dedyn.io {
# Needed for API call
import cors https://immich.your-server-name.dedyn.io
# Needed for iframe embedding
header {
Cross-Origin-Embedder-Policy require-corp
Cross-Origin-Resource-Policy cross-origin
}
@options method OPTIONS
respond @options 204
reverse_proxy http://localhost:2283
}
Other needs
Expose web server to internet
Using Caddy this becomes easy. Deploy the server with
immich.your-server-name.dedyn.io {
# Generate password storing hash with: caddy hash-password
basic_auth {
# Username "iib", password "bewegung"
iib $2a$14$basROD3Y0cLE.VqXd.h89.akCQDKzhp6IH9ND2CRFyEICkMrKn3AO
}
root /var/www/iib/
}
Alternatives
- Add support for https
- E.g. via Let’s encrypt
- Use user authentication via
- Authelia, or
- Authentik, or
- Client certificates, or
- Port forwarding over SSH (e.g. using Termius on Android)
List over SQL Queries
You can browse the folder queries/ if you want to see what SQL queries the app is using. This can be useful if you want to make some analytics in another other software.
Logotype
If you like Immer in Bewegung and want to link to the project from your homepage, you can use this button.
Code for embedding to copy
<a href="https://bewegung.app/" style="text-decoration:none;"><img src="img/get_bewegung.svg"></a>